]>
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
) {
157 Ext
.Array
.each(['GET', 'POST', 'PUT', 'DELETE'], function(method
) {
158 let info
= md
[method
];
162 usage
+= "<table><tr><td>HTTP: </td><td>"
163 + method
+ " " + real_path("/api2/json" + data
.path
) + "</td></tr>";
165 if (typeof cliusage
=== 'function') {
166 usage
+= cliusage(method
, real_path(data
.path
));
171 title
: 'Description',
172 html
: Ext
.htmlEncode(info
.description
),
182 if (info
.parameters
&& info
.parameters
.properties
) {
183 let pstore
= Ext
.create('Ext.data.Store', {
184 model
: 'pmx-param-schema',
188 groupField
: 'optional',
197 Ext
.Object
.each(info
.parameters
.properties
, function(name
, pdef
) {
204 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
205 enableGroupingMenu
: false,
206 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Required</tpl>',
212 features
: [groupingFeature
],
227 renderer
: render_type
,
232 dataIndex
: 'default',
238 renderer
: render_format
,
242 header
: 'Description',
243 dataIndex
: 'description',
244 renderer
: render_description
,
252 let retinf
= info
.returns
;
253 let rtype
= retinf
.type
;
254 if (!rtype
&& retinf
.items
) {rtype
= 'array';}
255 if (!rtype
) {rtype
= 'object';}
257 let rpstore
= Ext
.create('Ext.data.Store', {
258 model
: 'pmx-param-schema',
262 groupField
: 'optional',
272 if (rtype
=== 'array' && retinf
.items
.properties
) {
273 properties
= retinf
.items
.properties
;
276 if (rtype
=== 'object' && retinf
.properties
) {
277 properties
= retinf
.properties
;
280 Ext
.Object
.each(properties
, function(name
, pdef
) {
287 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
288 enableGroupingMenu
: false,
289 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Obligatory</tpl>',
293 returnhtml
= '<pre>items: ' + Ext
.htmlEncode(JSON
.stringify(retinf
.items
, null, 4)) + '</pre>';
296 if (retinf
.properties
) {
297 returnhtml
= returnhtml
|| '';
298 returnhtml
+= '<pre>properties:' + Ext
.htmlEncode(JSON
.stringify(retinf
.properties
, null, 4)) + '</pre>';
301 let rawSection
= Ext
.create('Ext.panel.Panel', {
302 bodyPadding
: '0px 10px 10px 10px',
309 title
: 'Returns: ' + rtype
,
310 features
: [groupingFeature
],
325 renderer
: render_type
,
330 dataIndex
: 'default',
336 renderer
: render_format
,
340 header
: 'Description',
341 dataIndex
: 'description',
342 renderer
: render_description
,
350 handler: function(btn
) {
351 rawSection
.setVisible(!rawSection
.isVisible());
352 btn
.setText(rawSection
.isVisible() ? 'Hide RAW' : 'Show RAW');
358 sections
.push(rawSection
);
361 if (!data
.path
.match(/\/_upgrade_/)) {
364 if (!info
.permissions
) {
365 permhtml
= "Root only.";
367 if (info
.permissions
.description
) {
368 permhtml
+= "<div style='white-space:pre-wrap;padding-bottom:10px;'>" +
369 Ext
.htmlEncode(info
.permissions
.description
) + "</div>";
371 permhtml
+= permission_text(info
.permissions
);
374 if (info
.allowtoken
!== undefined && !info
.allowtoken
) {
375 permhtml
+= "<br />This API endpoint is not available for API tokens.";
379 title
: 'Required permissions',
396 let ct
= Ext
.getCmp('docview');
397 ct
.setTitle("Path: " + real_path(data
.path
));
403 Ext
.define('Ext.form.SearchField', {
404 extend
: 'Ext.form.field.Text',
405 alias
: 'widget.searchfield',
407 emptyText
: 'Search...',
413 'change': function() {
414 let value
= this.getValue();
415 if (!Ext
.isEmpty(value
)) {
428 let tree
= Ext
.create('Ext.tree.Panel', {
429 title
: 'Resource Tree',
432 xtype
: 'searchfield',
438 tooltip
: 'Expand all',
439 tooltipType
: 'title',
440 callback
: (tree
) => tree
.expandAll(),
444 tooltip
: 'Collapse all',
445 tooltipType
: 'title',
446 callback
: (tree
) => tree
.collapseAll(),
456 selectionchange: function(v
, selections
) {
457 if (!selections
[0]) {return;}
458 let rec
= selections
[0];
459 render_docu(rec
.data
);
460 location
.hash
= '#' + rec
.data
.path
;
465 Ext
.create('Ext.container.Viewport', {
467 renderTo
: Ext
.getBody(),
472 title
: 'Documentation',
482 let deepLink = function() {
483 let path
= window
.location
.hash
.substring(1).replace(/\/\s*$/, '');
484 let endpoint
= store
.findNode('path', path
);
487 tree
.getSelectionModel().select(endpoint
);
488 tree
.expandPath(endpoint
.getPath());
489 render_docu(endpoint
.data
);
492 window
.onhashchange
= deepLink
;