]>
git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/api-viewer/APIViewer.js
3 Ext
.onReady(function() {
4 Ext
.define('pmx-param-schema', {
5 extend
: 'Ext.data.Model',
7 'name', 'type', 'typetext', 'description', 'verbose_description',
8 'enum', 'minimum', 'maximum', 'minLength', 'maxLength',
9 'pattern', 'title', 'requires', 'format', 'default',
10 'disallow', 'extends', 'links',
18 let store
= Ext
.define('pmx-updated-treestore', {
19 extend
: 'Ext.data.TreeStore',
20 model
: Ext
.define('pmx-api-doc', {
21 extend
: 'Ext.data.Model',
23 'path', 'info', 'text',
38 doFilter: function(node
) {
39 this.filterNodes(node
, this.getFilters().getFilterFn(), true);
42 filterNodes: function(node
, filterFn
, parentVisible
) {
44 bottomUpFiltering
= me
.filterer
=== 'bottomup',
45 match
= filterFn(node
) && parentVisible
|| (node
.isRoot() && !me
.getRootVisible()),
46 childNodes
= node
.childNodes
,
47 len
= childNodes
&& childNodes
.length
, i
, matchingChildren
;
50 for (i
= 0; i
< len
; ++i
) {
51 matchingChildren
= me
.filterNodes(childNodes
[i
], filterFn
, match
|| bottomUpFiltering
) || matchingChildren
;
53 if (bottomUpFiltering
) {
54 match
= matchingChildren
|| match
;
58 node
.set("visible", match
, me
._silentOptions
);
64 let render_description = function(value
, metaData
, record
) {
65 let pdef
= record
.data
;
67 value
= pdef
.verbose_description
|| value
;
69 // TODO: try to render asciidoc correctly
71 metaData
.style
= 'white-space:pre-wrap;';
73 return Ext
.htmlEncode(value
);
76 let render_type = function(value
, metaData
, record
) {
77 let pdef
= record
.data
;
79 return pdef
.enum ? 'enum' : pdef
.type
|| 'string';
82 let render_simple_format = function(pdef
, type_fallback
) {
83 if (pdef
.typetext
) {return pdef
.typetext
;}
85 if (pdef
.enum) {return pdef
.enum.join(' | ');}
87 if (pdef
.format
) {return pdef
.format
;}
89 if (pdef
.pattern
) {return pdef
.pattern
;}
91 if (pdef
.type
=== 'boolean') {return `<true|false>`;}
93 if (type_fallback
&& pdef
.type
) {return `<${pdef.type}>`;}
96 let render_format = function(value
, metaData
, record
) {
97 let pdef
= record
.data
;
99 metaData
.style
= 'white-space:normal;';
101 if (pdef
.type
=== 'array' && pdef
.items
) {
102 let format
= render_simple_format(pdef
.items
, true);
103 return `[${Ext.htmlEncode(format)}, ...]`;
106 return Ext
.htmlEncode(render_simple_format(pdef
) || '');
109 let real_path = function(path
) {
110 return path
.replace(/^.*\/_upgrade_(\/)?/, "/");
113 let permission_text = function(permission
) {
116 if (permission
.user
) {
117 if (!permission
.description
) {
118 if (permission
.user
=== 'world') {
119 permhtml
+= "Accessible without any authentication.";
120 } else if (permission
.user
=== 'all') {
121 permhtml
+= "Accessible by all authenticated users.";
123 permhtml
+= 'Onyl accessible by user "' +
124 permission
.user
+ '"';
127 } else if (permission
.check
) {
128 permhtml
+= "<pre>Check: " +
129 Ext
.htmlEncode(Ext
.JSON
.encode(permission
.check
)) + "</pre>";
130 } else if (permission
.userParam
) {
131 permhtml
+= `<div>Check if user matches parameter '${permission.userParam}'`;
132 } else if (permission
.or
) {
133 permhtml
+= "<div>Or<div style='padding-left: 10px;'>";
134 Ext
.Array
.each(permission
.or
, function(sub_permission
) {
135 permhtml
+= permission_text(sub_permission
);
137 permhtml
+= "</div></div>";
138 } else if (permission
.and
) {
139 permhtml
+= "<div>And<div style='padding-left: 10px;'>";
140 Ext
.Array
.each(permission
.and
, function(sub_permission
) {
141 permhtml
+= permission_text(sub_permission
);
143 permhtml
+= "</div></div>";
145 //console.log(permission);
146 permhtml
+= "Unknown syntax!";
152 let render_docu = function(data
) {
155 // console.dir(data);
166 Ext
.Array
.each(['GET', 'POST', 'PUT', 'DELETE'], function(method
) {
167 let info
= md
[method
];
171 usage
+= "<table><tr><td>HTTP: </td><td>"
172 + method
+ " " + real_path("/api2/json" + data
.path
) + "</td></tr>";
174 if (typeof cliusage
=== 'function') {
175 usage
+= cliusage(method
, real_path(data
.path
));
180 title
: 'Description',
181 html
: Ext
.htmlEncode(info
.description
),
191 if (info
.parameters
&& info
.parameters
.properties
) {
192 let pstore
= Ext
.create('Ext.data.Store', {
193 model
: 'pmx-param-schema',
197 groupField
: 'optional',
206 Ext
.Object
.each(info
.parameters
.properties
, function(name
, pdef
) {
213 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
214 enableGroupingMenu
: false,
215 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Required</tpl>',
221 features
: [groupingFeature
],
236 renderer
: render_type
,
241 dataIndex
: 'default',
247 renderer
: render_format
,
251 header
: 'Description',
252 dataIndex
: 'description',
253 renderer
: render_description
,
261 let retinf
= info
.returns
;
262 let rtype
= retinf
.type
;
263 if (!rtype
&& retinf
.items
) {rtype
= 'array';}
264 if (!rtype
) {rtype
= 'object';}
266 let rpstore
= Ext
.create('Ext.data.Store', {
267 model
: 'pmx-param-schema',
271 groupField
: 'optional',
281 if (rtype
=== 'array' && retinf
.items
.properties
) {
282 properties
= retinf
.items
.properties
;
285 if (rtype
=== 'object' && retinf
.properties
) {
286 properties
= retinf
.properties
;
289 Ext
.Object
.each(properties
, function(name
, pdef
) {
296 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
297 enableGroupingMenu
: false,
298 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Obligatory</tpl>',
302 returnhtml
= '<pre>items: ' + Ext
.htmlEncode(JSON
.stringify(retinf
.items
, null, 4)) + '</pre>';
305 if (retinf
.properties
) {
306 returnhtml
= returnhtml
|| '';
307 returnhtml
+= '<pre>properties:' + Ext
.htmlEncode(JSON
.stringify(retinf
.properties
, null, 4)) + '</pre>';
310 let rawSection
= Ext
.create('Ext.panel.Panel', {
311 bodyPadding
: '0px 10px 10px 10px',
318 title
: 'Returns: ' + rtype
,
319 features
: [groupingFeature
],
334 renderer
: render_type
,
339 dataIndex
: 'default',
345 renderer
: render_format
,
349 header
: 'Description',
350 dataIndex
: 'description',
351 renderer
: render_description
,
359 handler: function(btn
) {
360 rawSection
.setVisible(!rawSection
.isVisible());
361 btn
.setText(rawSection
.isVisible() ? 'Hide RAW' : 'Show RAW');
367 sections
.push(rawSection
);
370 if (!data
.path
.match(/\/_upgrade_/)) {
373 if (!info
.permissions
) {
374 permhtml
= "Root only.";
376 if (info
.permissions
.description
) {
377 permhtml
+= "<div style='white-space:pre-wrap;padding-bottom:10px;'>" +
378 Ext
.htmlEncode(info
.permissions
.description
) + "</div>";
380 permhtml
+= permission_text(info
.permissions
);
383 if (info
.allowtoken
!== undefined && !info
.allowtoken
) {
384 permhtml
+= "<br />This API endpoint is not available for API tokens.";
388 title
: 'Required permissions',
405 let ct
= Ext
.getCmp('docview');
406 ct
.setTitle("Path: " + real_path(data
.path
));
412 Ext
.define('Ext.form.SearchField', {
413 extend
: 'Ext.form.field.Text',
414 alias
: 'widget.searchfield',
416 emptyText
: 'Search...',
422 'change': function() {
423 let value
= this.getValue();
424 if (!Ext
.isEmpty(value
)) {
437 let tree
= Ext
.create('Ext.tree.Panel', {
438 title
: 'Resource Tree',
441 xtype
: 'searchfield',
447 tooltip
: 'Expand all',
448 tooltipType
: 'title',
449 callback
: (tree
) => tree
.expandAll(),
453 tooltip
: 'Collapse all',
454 tooltipType
: 'title',
455 callback
: (tree
) => tree
.collapseAll(),
465 selectionchange: function(v
, selections
) {
466 if (!selections
[0]) {return;}
467 let rec
= selections
[0];
468 render_docu(rec
.data
);
469 location
.hash
= '#' + rec
.data
.path
;
474 Ext
.create('Ext.container.Viewport', {
476 renderTo
: Ext
.getBody(),
481 title
: 'Documentation',
491 let deepLink = function() {
492 let path
= window
.location
.hash
.substring(1).replace(/\/\s*$/, '');
493 let endpoint
= store
.findNode('path', path
);
496 tree
.getSelectionModel().select(endpoint
);
497 tree
.expandPath(endpoint
.getPath());
498 render_docu(endpoint
.data
);
501 window
.onhashchange
= deepLink
;