openapi-explorer 0.9.310 → 0.9.314

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/dist/{openapi-explorer.min.js → browser/openapi-explorer.min.js} +3 -3
  3. package/dist/{openapi-explorer.min.js.map → browser/openapi-explorer.min.js.map} +0 -0
  4. package/dist/es/components/api-request.js +936 -0
  5. package/dist/es/components/api-response.js +184 -0
  6. package/dist/es/components/json-tree.js +67 -0
  7. package/{src → dist/es}/components/mime-types.js +11 -17
  8. package/dist/es/components/schema-table.js +156 -0
  9. package/dist/es/components/schema-tree.js +191 -0
  10. package/dist/es/components/tag-input.js +67 -0
  11. package/{src → dist/es}/openapi-explorer-oauth-handler.js +2 -2
  12. package/{src → dist/es}/openapi-explorer.js +364 -371
  13. package/dist/es/styles/advanced-search-styles.js +2 -0
  14. package/dist/es/styles/api-request-styles.js +2 -0
  15. package/dist/es/styles/border-styles.js +2 -0
  16. package/dist/es/styles/endpoint-styles.js +2 -0
  17. package/dist/es/styles/flex-styles.js +2 -0
  18. package/dist/es/styles/font-styles.js +2 -0
  19. package/dist/es/styles/info-styles.js +2 -0
  20. package/dist/es/styles/input-styles.js +4 -0
  21. package/dist/es/styles/nav-styles.js +2 -0
  22. package/dist/es/styles/prism-styles.js +2 -0
  23. package/dist/es/styles/schema-styles.js +2 -0
  24. package/dist/es/styles/tab-styles.js +2 -0
  25. package/dist/es/styles/table-styles.js +2 -0
  26. package/dist/es/templates/advance-search-template.js +37 -0
  27. package/dist/es/templates/callback-template.js +7 -0
  28. package/dist/es/templates/code-samples-template.js +26 -0
  29. package/dist/es/templates/components-template.js +17 -0
  30. package/dist/es/templates/endpoint-template.js +94 -0
  31. package/dist/es/templates/expanded-endpoint-template.js +32 -0
  32. package/{src → dist/es}/templates/focused-endpoint-template.js +15 -15
  33. package/dist/es/templates/navbar-template.js +46 -0
  34. package/dist/es/templates/overview-template.js +9 -0
  35. package/dist/es/templates/responsiveViewMainBodyTemplate.js +30 -0
  36. package/dist/es/templates/security-scheme-template.js +330 -0
  37. package/dist/es/templates/server-template.js +42 -0
  38. package/{src → dist/es}/utils/color-utils.js +53 -16
  39. package/{src → dist/es}/utils/common-utils.js +18 -18
  40. package/{src → dist/es}/utils/schema-utils.js +248 -124
  41. package/{src → dist/es}/utils/spec-parser.js +112 -71
  42. package/dist/es/utils/theme.js +75 -0
  43. package/{src → dist/es}/utils/xml/xml.js +41 -38
  44. package/dist/lib/components/api-request.js +957 -0
  45. package/dist/lib/components/api-response.js +206 -0
  46. package/dist/lib/components/json-tree.js +82 -0
  47. package/dist/lib/components/mime-types.js +70 -0
  48. package/dist/lib/components/schema-table.js +170 -0
  49. package/dist/lib/components/schema-tree.js +206 -0
  50. package/dist/lib/components/tag-input.js +76 -0
  51. package/dist/lib/openapi-explorer-oauth-handler.js +19 -0
  52. package/dist/lib/openapi-explorer.js +817 -0
  53. package/dist/lib/styles/advanced-search-styles.js +10 -0
  54. package/dist/lib/styles/api-request-styles.js +10 -0
  55. package/dist/lib/styles/border-styles.js +10 -0
  56. package/dist/lib/styles/endpoint-styles.js +10 -0
  57. package/dist/lib/styles/flex-styles.js +10 -0
  58. package/dist/lib/styles/font-styles.js +10 -0
  59. package/dist/lib/styles/info-styles.js +10 -0
  60. package/dist/lib/styles/input-styles.js +11 -0
  61. package/dist/lib/styles/nav-styles.js +10 -0
  62. package/dist/lib/styles/prism-styles.js +10 -0
  63. package/dist/lib/styles/schema-styles.js +10 -0
  64. package/dist/lib/styles/tab-styles.js +10 -0
  65. package/dist/lib/styles/table-styles.js +10 -0
  66. package/dist/lib/templates/advance-search-template.js +42 -0
  67. package/dist/lib/templates/callback-template.js +12 -0
  68. package/dist/lib/templates/code-samples-template.js +36 -0
  69. package/dist/lib/templates/components-template.js +27 -0
  70. package/dist/lib/templates/endpoint-template.js +111 -0
  71. package/dist/lib/templates/expanded-endpoint-template.js +48 -0
  72. package/dist/lib/templates/focused-endpoint-template.js +95 -0
  73. package/dist/lib/templates/navbar-template.js +54 -0
  74. package/dist/lib/templates/overview-template.js +16 -0
  75. package/dist/lib/templates/responsiveViewMainBodyTemplate.js +47 -0
  76. package/dist/lib/templates/security-scheme-template.js +342 -0
  77. package/dist/lib/templates/server-template.js +49 -0
  78. package/dist/lib/utils/color-utils.js +112 -0
  79. package/dist/lib/utils/common-utils.js +156 -0
  80. package/dist/lib/utils/schema-utils.js +743 -0
  81. package/dist/lib/utils/spec-parser.js +361 -0
  82. package/dist/lib/utils/theme.js +84 -0
  83. package/dist/lib/utils/xml/xml.js +239 -0
  84. package/package.json +19 -6
  85. package/dist/openapi-explorer.min.js.LICENSE.txt +0 -71
  86. package/dist/openapi-explorer.min.js.LICENSE.txt.gz +0 -0
  87. package/dist/openapi-explorer.min.js.gz +0 -0
  88. package/dist/openapi-explorer.min.js.map.gz +0 -0
  89. package/dist/report.html +0 -38
  90. package/src/components/api-request.js +0 -1244
  91. package/src/components/api-response.js +0 -340
  92. package/src/components/json-tree.js +0 -129
  93. package/src/components/schema-table.js +0 -250
  94. package/src/components/schema-tree.js +0 -280
  95. package/src/components/tag-input.js +0 -109
  96. package/src/styles/advanced-search-styles.js +0 -84
  97. package/src/styles/api-request-styles.js +0 -111
  98. package/src/styles/border-styles.js +0 -24
  99. package/src/styles/css/main.css +0 -24
  100. package/src/styles/endpoint-styles.js +0 -222
  101. package/src/styles/flex-styles.js +0 -15
  102. package/src/styles/font-styles.js +0 -266
  103. package/src/styles/info-styles.js +0 -20
  104. package/src/styles/input-styles.js +0 -236
  105. package/src/styles/nav-styles.js +0 -141
  106. package/src/styles/prism-styles.js +0 -107
  107. package/src/styles/schema-styles.js +0 -121
  108. package/src/styles/tab-styles.js +0 -44
  109. package/src/styles/table-styles.js +0 -48
  110. package/src/templates/advance-search-template.js +0 -81
  111. package/src/templates/callback-template.js +0 -63
  112. package/src/templates/code-samples-template.js +0 -35
  113. package/src/templates/components-template.js +0 -43
  114. package/src/templates/endpoint-template.js +0 -175
  115. package/src/templates/expanded-endpoint-template.js +0 -104
  116. package/src/templates/navbar-template.js +0 -175
  117. package/src/templates/overview-template.js +0 -58
  118. package/src/templates/responsiveViewMainBodyTemplate.js +0 -72
  119. package/src/templates/security-scheme-template.js +0 -487
  120. package/src/templates/server-template.js +0 -106
  121. package/src/utils/theme.js +0 -163
@@ -0,0 +1,184 @@
1
+ import { LitElement, html, css } from 'lit-element';
2
+ import { marked } from 'marked';
3
+ import { unsafeHTML } from 'lit-html/directives/unsafe-html';
4
+ import { schemaInObjectNotation, generateExample } from '../utils/schema-utils';
5
+ import FontStyles from '../styles/font-styles';
6
+ import FlexStyles from '../styles/flex-styles';
7
+ import TableStyles from '../styles/table-styles';
8
+ import InputStyles from '../styles/input-styles';
9
+ import TabStyles from '../styles/tab-styles';
10
+ import BorderStyles from '../styles/border-styles';
11
+ import './schema-tree';
12
+ import './schema-table';
13
+ export default class ApiResponse extends LitElement {
14
+ constructor() {
15
+ super();
16
+ this.selectedStatus = '';
17
+ this.headersForEachRespStatus = {};
18
+ this.mimeResponsesForEachStatus = {};
19
+ this.activeSchemaTab = 'model';
20
+ }
21
+
22
+ static get properties() {
23
+ return {
24
+ callback: {
25
+ type: String
26
+ },
27
+ responses: {
28
+ type: Object
29
+ },
30
+ parser: {
31
+ type: Object
32
+ },
33
+ schemaStyle: {
34
+ type: String,
35
+ attribute: 'schema-style'
36
+ },
37
+ renderStyle: {
38
+ type: String,
39
+ attribute: 'render-style'
40
+ },
41
+ selectedStatus: {
42
+ type: String,
43
+ attribute: 'selected-status'
44
+ },
45
+ selectedMimeType: {
46
+ type: String,
47
+ attribute: 'selected-mime-type'
48
+ },
49
+ activeSchemaTab: {
50
+ type: String,
51
+ attribute: 'active-schema-tab'
52
+ },
53
+ schemaExpandLevel: {
54
+ type: Number,
55
+ attribute: 'schema-expand-level'
56
+ },
57
+ schemaDescriptionExpanded: {
58
+ type: String,
59
+ attribute: 'schema-description-expanded'
60
+ },
61
+ schemaHideWriteOnly: {
62
+ type: String,
63
+ attribute: 'schema-hide-write-only'
64
+ }
65
+ };
66
+ }
67
+
68
+ static get styles() {
69
+ return [FontStyles, FlexStyles, TabStyles, TableStyles, InputStyles, BorderStyles, css`.resp-head{vertical-align:middle;padding:16px 0 8px}.resp-head.divider{border-top:1px solid var(--border-color);margin-top:10px}.resp-status{font-weight:700;font-size:calc(var(--font-size-small) + 1px)}.resp-descr{font-size:calc(var(--font-size-small) + 1px)}.top-gap{margin-top:16px}.example-panel{font-size:var(--font-size-small);margin:0}.generic-tree{background:#333;color:#fff}.example-panel.generic-tree{margin-top:8px}pre.generic-tree{border:none;padding:8px 10px 10px}.example-panel select{margin-left:8px;padding-top:8px;min-width:100px;max-width:100%}.example-panel .example{padding-top:8px}.focused-mode,.read-mode{padding-top:24px;margin-top:12px;border-top:1px dashed var(--border-color)}`];
70
+ }
71
+
72
+ render() {
73
+ return html` <div class="col regular-font response-panel ${this.renderStyle}-mode"> <div class="${this.callback === 'true' ? 'tiny-title' : 'req-res-title'}"> ${this.callback === 'true' ? 'CALLBACK RESPONSE' : 'RESPONSE'} </div> <div> ${this.responseTemplate()} <div> </div> </div></div>`;
74
+ }
75
+
76
+ resetSelection() {
77
+ this.selectedStatus = '';
78
+ this.selectedMimeType = '';
79
+ }
80
+ /* eslint-disable indent */
81
+
82
+
83
+ responseTemplate() {
84
+ if (!this.responses) {
85
+ return '';
86
+ }
87
+
88
+ for (const statusCode in this.responses) {
89
+ if (!this.selectedStatus) {
90
+ this.selectedStatus = statusCode;
91
+ }
92
+
93
+ const allMimeResp = {};
94
+
95
+ for (const mimeResp in this.responses[statusCode] && this.responses[statusCode].content) {
96
+ const mimeRespObj = this.responses[statusCode].content[mimeResp];
97
+
98
+ if (!this.selectedMimeType) {
99
+ this.selectedMimeType = mimeResp;
100
+ } // Generate Schema
101
+
102
+
103
+ const schemaTree = schemaInObjectNotation(mimeRespObj.schema, {}); // Generate Example
104
+
105
+ const respExamples = generateExample(mimeRespObj.examples || '', mimeRespObj.example || '', mimeRespObj.schema, mimeResp, true, false, mimeResp.includes('json') ? 'json' : 'text');
106
+ allMimeResp[mimeResp] = {
107
+ description: this.responses[statusCode].description,
108
+ examples: respExamples,
109
+ selectedExample: respExamples[0] && respExamples[0].exampleId || '',
110
+ schemaTree
111
+ };
112
+ } // Headers for each response status
113
+
114
+
115
+ const tempHeaders = [];
116
+
117
+ for (const key in this.responses[statusCode] && this.responses[statusCode].headers) {
118
+ tempHeaders.push({
119
+ name: key,
120
+ ...this.responses[statusCode].headers[key]
121
+ });
122
+ }
123
+
124
+ this.headersForEachRespStatus[statusCode] = tempHeaders;
125
+ this.mimeResponsesForEachStatus[statusCode] = allMimeResp;
126
+ }
127
+
128
+ return html`<div class="row" style="flex-wrap:wrap"> ${Object.keys(this.responses).map(respStatus => html` ${respStatus === '$$ref' // Swagger-Client parser creates '$$ref' object if JSON references are used to create responses - this should be ignored
129
+ ? '' : html` <button @click="${() => {
130
+ this.selectedStatus = respStatus;
131
+
132
+ if (this.responses[respStatus].content && Object.keys(this.responses[respStatus].content)[0]) {
133
+ this.selectedMimeType = Object.keys(this.responses[respStatus].content)[0];
134
+ } else {
135
+ this.selectedMimeType = undefined;
136
+ }
137
+ }}" class="m-btn small ${this.selectedStatus === respStatus ? 'primary' : ''}" part="btn--resp ${this.selectedStatus === respStatus ? 'btn-fill--resp' : 'btn-outline--resp'} btn-response-status" style="margin:8px 4px 0 0"> ${respStatus} </button>`}`)} </div> ${Object.keys(this.responses).map(status => html` <div style="display:${status === this.selectedStatus ? 'block' : 'none'}"> <div class="top-gap"> <span class="resp-descr m-markdown">${unsafeHTML(marked(this.responses[status] && this.responses[status].description || ''))}</span> ${this.headersForEachRespStatus[status] && this.headersForEachRespStatus[status].length > 0 ? html`${this.responseHeaderListTemplate(this.headersForEachRespStatus[status])}` : ''} </div> ${Object.keys(this.mimeResponsesForEachStatus[status]).length === 0 ? '' : html` <div class="tab-panel col"> <div class="tab-buttons row" @click="${e => {
138
+ if (e.target.tagName.toLowerCase() === 'button') {
139
+ this.activeSchemaTab = e.target.dataset.tab;
140
+ }
141
+ }}"> <button class="tab-btn ${this.activeSchemaTab === 'model' ? 'active' : ''}" data-tab="model">MODEL</button> <button class="tab-btn ${this.activeSchemaTab === 'body' ? 'active' : ''}" data-tab="body">EXAMPLE</button> <div style="flex:1"></div> ${Object.keys(this.mimeResponsesForEachStatus[status]).length === 1 ? html`<span class="small-font-size gray-text" style="align-self:center;margin-top:8px"> ${Object.keys(this.mimeResponsesForEachStatus[status])[0]} </span>` : html`${this.mimeTypeDropdownTemplate(Object.keys(this.mimeResponsesForEachStatus[status]))}`} </div> ${this.activeSchemaTab === 'body' ? html`<div class="tab-content col" style="flex:1"> ${this.mimeExampleTemplate(this.mimeResponsesForEachStatus[status][this.selectedMimeType])} </div>` : html`<div class="tab-content col" style="flex:1"> ${this.mimeSchemaTemplate(this.mimeResponsesForEachStatus[status][this.selectedMimeType])} </div>`} </div> `}</div>`)} `;
142
+ }
143
+
144
+ responseHeaderListTemplate(respHeaders) {
145
+ return html` <div style="padding:16px 0 8px 0" class="resp-headers small-font-size bold-text">RESPONSE HEADERS</div> <table role="presentation" style="border-collapse:collapse;margin-bottom:16px;border:1px solid var(--border-color);border-radius:var(--border-radius)" class="small-font-size mono-font"> ${respHeaders.map(v => html` <tr> <td style="padding:8px;vertical-align:baseline;min-width:120px;border-top:1px solid var(--light-border-color);text-overflow:ellipsis"> ${v.name || ''} </td> <td style="padding:4px;vertical-align:baseline;padding:0 5px;border-top:1px solid var(--light-border-color);text-overflow:ellipsis"> ${v.schema.type || ''} </td> <td style="padding:8px;vertical-align:baseline;border-top:1px solid var(--light-border-color);text-overflow:ellipsis"> <div class="m-markdown-small regular-font">${unsafeHTML(marked(v.description || ''))}</div> </td> <td style="padding:8px;vertical-align:baseline;border-top:1px solid var(--light-border-color);text-overflow:ellipsis"> ${v.schema.example || ''} </td> </tr> `)} </table>`;
146
+ }
147
+
148
+ mimeTypeDropdownTemplate(mimeTypes) {
149
+ return html` <select aria-label="mime type" @change="${e => {
150
+ this.selectedMimeType = e.target.value;
151
+ }}" style="margin-bottom:-1px;z-index:1"> ${mimeTypes.map(mimeType => html`<option value="${mimeType}" ?selected="${mimeType === this.selectedMimeType}"> ${mimeType} </option>`)} </select>`;
152
+ }
153
+
154
+ onSelectExample(e) {
155
+ const exampleContainerEl = e.target.closest('.example-panel');
156
+ const exampleEls = [...exampleContainerEl.querySelectorAll('.example')];
157
+ exampleEls.forEach(v => {
158
+ v.style.display = v.dataset.example === e.target.value ? 'block' : 'none';
159
+ });
160
+ }
161
+
162
+ mimeExampleTemplate(mimeRespDetails) {
163
+ if (!mimeRespDetails) {
164
+ return html` <pre style="color:var(--red)" class="example-panel border-top"> No example provided </pre> `;
165
+ }
166
+
167
+ return html` ${mimeRespDetails.examples.length === 1 ? html` ${mimeRespDetails.examples[0].exampleSummary && mimeRespDetails.examples[0].exampleSummary.length > 80 ? html`<div style="padding:4px 0"> ${mimeRespDetails.examples[0].exampleSummary} </div>` : ''} ${mimeRespDetails.examples[0].exampleDescription ? html`<div class="m-markdown-small" style="padding:4px 0"> ${unsafeHTML(marked(mimeRespDetails.examples[0].exampleDescription || ''))} </div>` : ''} ${mimeRespDetails.examples[0].exampleFormat === 'json' ? html` <json-tree render-style="${this.renderStyle}" .data="${mimeRespDetails.examples[0].exampleValue}" class="example-panel pad-top-8"></json-tree>` : html` <pre class="example-panel generic-tree border-top pad-top-8">${mimeRespDetails.examples[0].exampleValue}</pre> `}` : html` <span class="example-panel generic-tree ${this.renderStyle === 'read' ? 'border pad-8-16' : 'border-top pad-top-8'}"> <select aria-label="response body example" @change="${e => this.onSelectExample(e)}"> ${mimeRespDetails.examples.map(v => html`<option value="${v.exampleId}" ?selected="${v.exampleId === mimeRespDetails.selectedExample}"> ${v.exampleSummary.length > 80 ? v.exampleId : v.exampleSummary} </option>`)} </select> ${mimeRespDetails.examples.map(v => html` <div class="example" data-example="${v.exampleId}" style="display:${v.exampleId === mimeRespDetails.selectedExample ? 'block' : 'none'}"> ${v.exampleSummary && v.exampleSummary.length > 80 ? html`<div style="padding:4px 0"> ${v.exampleSummary} </div>` : ''} ${v.exampleDescription ? html`<div class="m-markdown-small" style="padding:4px 0"> ${unsafeHTML(marked(v.exampleDescription || ''))} </div>` : ''} ${v.exampleFormat === 'json' ? html` <json-tree render-style="${this.renderStyle}" .data="${v.exampleValue}"></json-tree>` : html`<pre class="generic-tree">${v.exampleValue}</pre>`} </div> `)} </span> `} `;
168
+ }
169
+
170
+ mimeSchemaTemplate(mimeRespDetails) {
171
+ if (!mimeRespDetails) {
172
+ return html` <pre style="color:var(--red)" class="${this.renderStyle === 'read' ? 'border pad-8-16' : 'border-top'}"> Schema not found</pre> `;
173
+ }
174
+
175
+ return html` ${this.schemaStyle === 'table' ? html` <schema-table render-style="${this.renderStyle}" .data="${mimeRespDetails.schemaTree}" class="example-panel ${this.renderStyle === 'read' ? 'border pad-8-16' : 'border-top pad-top-8'}" schema-expand-level="${this.schemaExpandLevel}" schema-description-expanded="${this.schemaDescriptionExpanded}" schema-hide-read-only="false" schema-hide-write-only="${this.schemaHideWriteOnly}"> </schema-table>` : html` <schema-tree render-style="${this.renderStyle}" .data="${mimeRespDetails.schemaTree}" class="example-panel ${this.renderStyle === 'read' ? 'border pad-8-16' : 'pad-top-8'}" schema-expand-level="${this.schemaExpandLevel}" schema-description-expanded="${this.schemaDescriptionExpanded}" schema-hide-read-only="false" schema-hide-write-only="${this.schemaHideWriteOnly}"> </schema-tree>`}`;
176
+ }
177
+ /* eslint-enable indent */
178
+
179
+
180
+ } // Register the element with the browser
181
+
182
+ if (!customElements.get('openapi-explorer')) {
183
+ customElements.define('api-response', ApiResponse);
184
+ }
@@ -0,0 +1,67 @@
1
+ import { LitElement, html, css } from 'lit-element';
2
+ import { copyToClipboard } from '../utils/common-utils';
3
+ import FontStyles from '../styles/font-styles';
4
+ import BorderStyles from '../styles/border-styles';
5
+ import InputStyles from '../styles/input-styles';
6
+ export default class JsonTree extends LitElement {
7
+ static get properties() {
8
+ return {
9
+ data: {
10
+ type: Object
11
+ },
12
+ renderStyle: {
13
+ type: String,
14
+ attribute: 'render-style'
15
+ }
16
+ };
17
+ }
18
+
19
+ static get styles() {
20
+ return [FontStyles, BorderStyles, InputStyles, css`:host{display:flex}.json-tree{background:#333;color:#fff;padding:8px;min-height:30px;font-family:var(--font-mono);font-size:var(--font-size-small);overflow:hidden;word-break:break-all;flex:1;line-height:calc(var(--font-size-small) + 6px)}.open-bracket{display:inline-block;padding:0 20px 0 0;cursor:pointer;border:1px solid transparent;border-radius:3px}.open-bracket:hover{color:var(--primary-color);background-color:var(--hover-color);border:1px solid var(--border-color)}.inside-bracket{padding-left:16px;border-left:1px dotted var(--border-color)}.open-bracket.collapsed+.inside-bracket,.open-bracket.collapsed+.inside-bracket+.close-bracket{display:none}.string{color:var(--green)}.number{color:var(--blue)}.null{color:var(--red)}.boolean{color:var(--orange)}.object{color:#fff}.toolbar{position:absolute;right:6px;display:flex;align-items:center}`];
21
+ }
22
+ /* eslint-disable indent */
23
+
24
+
25
+ render() {
26
+ return html` <div class="json-tree"> <div class="toolbar"> <button class="toolbar-copy-btn" part="btn btn-fill" @click="${e => {
27
+ copyToClipboard(JSON.stringify(this.data, null, 2), e);
28
+ }}">Copy</button> </div> ${this.generateTree(this.data, true)} </div> `;
29
+ }
30
+
31
+ generateTree(data, isLast = false) {
32
+ if (data === null) {
33
+ return html`<div class="null" style="display:inline">null</div>`;
34
+ }
35
+
36
+ if (typeof data === 'object' && data instanceof Date === false) {
37
+ const detailType = Array.isArray(data) ? 'array' : 'pure_object';
38
+
39
+ if (Object.keys(data).length === 0) {
40
+ return html`${Array.isArray(data) ? '[ ],' : '{ },'}`;
41
+ }
42
+
43
+ return html` <div class="open-bracket expanded ${detailType === 'array' ? 'array' : 'object'}" @click="${this.toggleExpand}"> ${detailType === 'array' ? '[' : '{'}</div> <div class="inside-bracket"> ${Object.keys(data).map((key, i, a) => html` <div class="item"> ${detailType === 'pure_object' ? html`"${key}":` : ''} ${this.generateTree(data[key], i === a.length - 1)} </div>`)} </div> <div class="close-bracket">${detailType === 'array' ? ']' : '}'}${isLast ? '' : ','}</div> `;
44
+ }
45
+
46
+ return typeof data === 'string' || data instanceof Date ? html`<span class="${typeof data}">"${data}"</span>${isLast ? '' : ','}` : html`<span class="${typeof data}">${data}</span>${isLast ? '' : ','}`;
47
+ }
48
+ /* eslint-enable indent */
49
+
50
+
51
+ toggleExpand(e) {
52
+ const openBracketEl = e.target;
53
+
54
+ if (openBracketEl.classList.contains('expanded')) {
55
+ openBracketEl.classList.replace('expanded', 'collapsed');
56
+ e.target.innerHTML = e.target.classList.contains('array') ? '[...]' : '{...}';
57
+ } else {
58
+ openBracketEl.classList.replace('collapsed', 'expanded');
59
+ e.target.innerHTML = e.target.classList.contains('array') ? '[' : '{';
60
+ }
61
+ }
62
+
63
+ } // Register the element with the browser
64
+
65
+ if (!customElements.get('openapi-explorer')) {
66
+ customElements.define('json-tree', JsonTree);
67
+ }
@@ -1,5 +1,4 @@
1
- const db = require('mime-db');
2
-
1
+ import db from 'mime-db';
3
2
  const EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
4
3
  /**
5
4
  * Get the default extension for a MIME type.
@@ -11,12 +10,11 @@ const EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
11
10
  export default function extension(type) {
12
11
  if (!type || typeof type !== 'string') {
13
12
  return false;
14
- }
13
+ } // TODO: use media-typer
14
+
15
15
 
16
- // TODO: use media-typer
17
- const match = EXTRACT_TYPE_REGEXP.exec(type);
16
+ const match = EXTRACT_TYPE_REGEXP.exec(type); // get extensions
18
17
 
19
- // get extensions
20
18
  const exts = match && exports.extensions[match[1].toLowerCase()];
21
19
 
22
20
  if (!exts || !exts.length) {
@@ -25,7 +23,6 @@ export default function extension(type) {
25
23
 
26
24
  return exts[0];
27
25
  }
28
-
29
26
  const extensions = {};
30
27
  const types = {};
31
28
  populateMaps();
@@ -33,19 +30,17 @@ populateMaps();
33
30
  function populateMaps() {
34
31
  // source preference (least -> most)
35
32
  const preference = ['nginx', 'apache', undefined, 'iana'];
36
-
37
33
  Object.keys(db).forEach(function forEachMimeType(type) {
38
34
  const mime = db[type];
39
35
  const exts = mime.extensions;
40
36
 
41
37
  if (!exts || !exts.length) {
42
38
  return;
43
- }
39
+ } // mime -> extensions
40
+
44
41
 
45
- // mime -> extensions
46
- extensions[type] = exts;
42
+ extensions[type] = exts; // extension -> mime
47
43
 
48
- // extension -> mime
49
44
  for (let i = 0; i < exts.length; i++) {
50
45
  const extensionToTest = exts[i];
51
46
 
@@ -53,15 +48,14 @@ function populateMaps() {
53
48
  const from = preference.indexOf(db[types[extensionToTest]].source);
54
49
  const to = preference.indexOf(mime.source);
55
50
 
56
- if (types[extensionToTest] !== 'application/octet-stream'
57
- && (from > to || (from === to && types[extensionToTest].substr(0, 12) === 'application/'))) {
51
+ if (types[extensionToTest] !== 'application/octet-stream' && (from > to || from === to && types[extensionToTest].substr(0, 12) === 'application/')) {
58
52
  // skip the remapping
59
53
  continue;
60
54
  }
61
- }
55
+ } // set the extension -> mime
56
+
62
57
 
63
- // set the extension -> mime
64
58
  types[extensionToTest] = type;
65
59
  }
66
60
  });
67
- }
61
+ }
@@ -0,0 +1,156 @@
1
+ import { LitElement, html, css } from 'lit-element';
2
+ import { marked } from 'marked';
3
+ import { unsafeHTML } from 'lit-html/directives/unsafe-html';
4
+ import FontStyles from '../styles/font-styles';
5
+ import SchemaStyles from '../styles/schema-styles';
6
+ export default class SchemaTable extends LitElement {
7
+ static get properties() {
8
+ return {
9
+ schemaExpandLevel: {
10
+ type: Number,
11
+ attribute: 'schema-expand-level'
12
+ },
13
+ schemaDescriptionExpanded: {
14
+ type: String,
15
+ attribute: 'schema-description-expanded'
16
+ },
17
+ schemaHideReadOnly: {
18
+ type: String,
19
+ attribute: 'schema-hide-read-only'
20
+ },
21
+ schemaHideWriteOnly: {
22
+ type: String,
23
+ attribute: 'schema-hide-write-only'
24
+ },
25
+ data: {
26
+ type: Object
27
+ }
28
+ };
29
+ }
30
+
31
+ connectedCallback() {
32
+ super.connectedCallback();
33
+
34
+ if (!this.schemaExpandLevel || this.schemaExpandLevel < 1) {
35
+ this.schemaExpandLevel = 99999;
36
+ }
37
+
38
+ if (!this.schemaDescriptionExpanded || !'true false'.includes(this.schemaDescriptionExpanded)) {
39
+ this.schemaDescriptionExpanded = 'true';
40
+ }
41
+
42
+ if (!this.schemaHideReadOnly || !'true false'.includes(this.schemaHideReadOnly)) {
43
+ this.schemaHideReadOnly = 'true';
44
+ }
45
+
46
+ if (!this.schemaHideWriteOnly || !'true false'.includes(this.schemaHideWriteOnly)) {
47
+ this.schemaHideWriteOnly = 'true';
48
+ }
49
+ }
50
+
51
+ static get styles() {
52
+ return [FontStyles, SchemaStyles, css`.table{font-size:var(--font-size-small);text-align:left;line-height:calc(var(--font-size-small) + 6px)}.table .tr{width:calc(100% - 5px);padding:0 0 0 5px;border-bottom:1px dotted var(--light-border-color)}.table .td{padding:4px 0}.table .key{width:240px}.key.deprecated .key-label{text-decoration:line-through}.table .key-type{white-space:normal;width:150px}.collapsed-descr .tr{max-height:calc(var(--font-size-small) + var(--font-size-small) + 4px)}.obj-toggle{padding:0 2px;border-radius:2px;border:1px solid transparent;display:inline-block;margin-left:-16px;color:var(--primary-color);cursor:pointer;font-size:calc(var(--font-size-small) + 4px);font-family:var(--font-mono);background-clip:border-box}.obj-toggle:hover{border-color:var(--primary-color)}.tr.expanded+.object-body{display:block}.tr.collapsed+.object-body{display:none}`];
53
+ }
54
+ /* eslint-disable indent */
55
+
56
+
57
+ render() {
58
+ return html` <div class="table ${this.schemaDescriptionExpanded === 'true' ? 'expanded-descr' : 'collapsed-descr'}"> <div class="toolbar"> <div style="flex:1"></div> <div class="toolbar-item" @click="${() => {
59
+ this.schemaDescriptionExpanded = this.schemaDescriptionExpanded === 'true' ? 'false' : 'true';
60
+ }}"> ${this.schemaDescriptionExpanded === 'true' ? 'Collapse descriptions' : 'Expand descriptions'} </div> </div> ${this.data && this.data['::description'] ? html`<span class="m-markdown"> ${unsafeHTML(marked(this.data['::description'] || ''))}</span>` : ''} <div style="border:1px solid var(--light-border-color)"> <div style="display:flex;background-color:var(--bg2);padding:8px 4px;border-bottom:1px solid var(--light-border-color)"> <div class="key" style="font-family:var(--font-regular);font-weight:700;color:var(--fg)"> Field </div> <div class="key-type" style="font-family:var(--font-regular);font-weight:700;color:var(--fg)"> Type </div> <div class="key-descr" style="font-family:var(--font-regular);font-weight:700;color:var(--fg)"> Description </div> </div> ${this.data ? html`${this.generateTree(this.data['::type'] === 'array' ? this.data['::props'] : this.data, this.data['::type'])}` : ''} </div> </div> `;
61
+ }
62
+
63
+ generateTree(data, dataType = 'object', key = '', description = '', schemaLevel = 0, indentLevel = 0) {
64
+ const newSchemaLevel = data['::type'] && data['::type'].startsWith('xxx-of') ? schemaLevel : schemaLevel + 1;
65
+ const newIndentLevel = dataType === 'xxx-of-option' || data['::type'] === 'xxx-of-option' || key.startsWith('::OPTION') ? indentLevel : indentLevel + 1;
66
+ const leftPadding = 16 * newIndentLevel; // 2 space indentation at each level
67
+
68
+ if (!data) {
69
+ return html`<div class="null" style="display:inline">null</div>`;
70
+ }
71
+
72
+ if (Object.keys(data).length === 0) {
73
+ return html`<span class="td key object" style="padding-left:${leftPadding}px">${key}</span>`;
74
+ }
75
+
76
+ let keyLabel = '';
77
+ let keyDescr = '';
78
+ let isOneOfLabel = false;
79
+
80
+ if (key.startsWith('::ONE~OF') || key.startsWith('::ANY~OF')) {
81
+ keyLabel = key.replace('::', '').replace('~', ' ');
82
+ isOneOfLabel = true;
83
+ } else if (key.startsWith('::OPTION')) {
84
+ const parts = key.split('~');
85
+ keyLabel = parts[1];
86
+ keyDescr = parts[2];
87
+ } else {
88
+ keyLabel = key;
89
+ }
90
+
91
+ let detailObjType = '';
92
+
93
+ if (data['::type'] === 'object') {
94
+ if (dataType === 'array') {
95
+ detailObjType = 'array'; // Array of Object
96
+ } else {
97
+ detailObjType = 'object';
98
+ }
99
+ } else if (data['::type'] === 'array') {
100
+ if (dataType === 'array') {
101
+ detailObjType = 'array of array'; // Array of array
102
+ } else {
103
+ detailObjType = 'array';
104
+ }
105
+ }
106
+
107
+ if (typeof data === 'object') {
108
+ return html` ${newSchemaLevel >= 0 && key ? html` <div class="tr ${newSchemaLevel <= this.schemaExpandLevel ? 'expanded' : 'collapsed'} ${data['::type']}" data-obj="${keyLabel}"> <div class="td key ${data['::deprecated'] ? 'deprecated' : ''}" style="padding-left:${leftPadding}px"> ${keyLabel || keyDescr ? html` <span class="obj-toggle ${newSchemaLevel < this.schemaExpandLevel ? 'expanded' : 'collapsed'}" data-obj="${keyLabel}" @click="${e => this.toggleObjectExpand(e, keyLabel)}"> ${schemaLevel < this.schemaExpandLevel ? '-' : '+'} </span>` : ''} ${data['::type'] === 'xxx-of-option' || data['::type'] === 'xxx-of-array' || key.startsWith('::OPTION') ? html`<span class="xxx-of-key" style="margin-left:-6px">${keyLabel}</span><span class="${isOneOfLabel ? 'xxx-of-key' : 'xxx-of-descr'}">${keyDescr}</span>` : keyLabel.endsWith('*') ? html`<span class="key-label" style="display:inline-block;margin-left:-6px"> ${keyLabel.substring(0, keyLabel.length - 1)}</span><span style="color:var(--red)">*</span>` : html`<span class="key-label" style="display:inline-block;margin-left:-6px">${keyLabel === '::props' ? '' : keyLabel}</span>`} ${data['::type'] === 'xxx-of' && dataType === 'array' ? html`<span style="color:var(--primary-color)">ARRAY</span>` : ''} </div> <div class="td key-type">${(data['::type'] || '').includes('xxx-of') ? '' : detailObjType}</div> <div class="td key-descr m-markdown-small" style="line-height:1.7">${unsafeHTML(marked(description || ''))}</div> </div>` : html` ${data['::type'] === 'array' && dataType === 'array' ? html`<div class="tr"> <div class="td"> ${dataType} </div> </div>` : ''} `} <div class="object-body"> ${Array.isArray(data) && data[0] ? html`${this.generateTree(data[0], 'xxx-of-option', '::ARRAY~OF', '', newSchemaLevel, newIndentLevel)}` : html`${Object.keys(data).map(dataKey => ['::title', '::description', '::type', '::props', '::deprecated'].includes(dataKey) && data[dataKey]['::type'] !== 'array' && data[dataKey]['::type'] !== 'object' ? '' : html`${this.generateTree(data[dataKey]['::type'] === 'array' ? data[dataKey]['::props'] : data[dataKey], data[dataKey]['::type'], dataKey, data[dataKey]['::description'], newSchemaLevel, newIndentLevel)}`)}`} <div> </div></div>`;
109
+ } // For Primitive Data types
110
+
111
+
112
+ const {
113
+ type,
114
+ readOrWriteOnly,
115
+ constraint,
116
+ defaultValue,
117
+ allowedValues,
118
+ pattern,
119
+ schemaDescription,
120
+ schemaTitle,
121
+ deprecated
122
+ } = JSON.parse(data);
123
+
124
+ if (readOrWriteOnly === '🆁' && this.schemaHideReadOnly === 'true') {
125
+ return undefined;
126
+ }
127
+
128
+ if (readOrWriteOnly === '🆆' && this.schemaHideWriteOnly === 'true') {
129
+ return undefined;
130
+ }
131
+
132
+ const dataTypeCss = type.replace(/┃.*/g, '').replace(/[^a-zA-Z0-9+]/g, '').substring(0, 4).toLowerCase();
133
+ return html` <div class="tr primitive"> <div class="td key ${deprecated ? 'deprecated' : ''}" style="padding-left:${leftPadding}px"> ${keyLabel && keyLabel.endsWith('*') ? html`<span class="key-label">${keyLabel.substring(0, keyLabel.length - 1)}</span><span style="color:var(--red)">*</span>` : key.startsWith('::OPTION') ? html`<span class="xxx-of-key">${keyLabel}</span><span class="xxx-of-descr">${keyDescr}</span>` : html`${keyLabel ? html`<span class="key-label"> ${keyLabel}</span>` : html`<span class="xxx-of-descr">${schemaTitle}</span>`}`} </div> <div class="td key-type ${dataTypeCss}"> ${dataType === 'array' ? `${type}[]` : type} <span style="font-family:var(--font-mono)" title="${readOrWriteOnly === '🆁' && 'Read only attribute' || readOrWriteOnly === '🆆' && 'Write only attribute' || ''}"> ${readOrWriteOnly} </span> </div> <div class="td key-descr"> ${dataType === 'array' ? html`<span class="m-markdown-small">${unsafeHTML(marked(description))}</span>` : ''} ${schemaTitle || schemaDescription ? html`<span class="m-markdown-small">${unsafeHTML(marked(`${schemaTitle ? `**${schemaTitle}:** ` : ''}${schemaDescription || ''}`))}</span>` : ''} ${constraint ? html`<div style="display:inline-block;line-break:anywhere;margin-right:8px"> <span class="bold-text">Constraints: </span> ${constraint}</div>` : ''} ${defaultValue ? html`<div style="display:inline-block;line-break:anywhere;margin-right:8px"> <span class="bold-text">Default: </span>${defaultValue}</div>` : ''} ${allowedValues ? html`<div style="display:inline-block;line-break:anywhere;margin-right:8px"> <span class="bold-text">Allowed: </span>${allowedValues}</div>` : ''} ${pattern ? html`<div style="display:inline-block;line-break:anywhere;margin-right:8px"> <span class="bold-text">Pattern: </span>${pattern}</div>` : ''} </div> </div> `;
134
+ }
135
+ /* eslint-enable indent */
136
+
137
+
138
+ toggleObjectExpand(e) {
139
+ const rowEl = e.target.closest('.tr');
140
+
141
+ if (rowEl.classList.contains('expanded')) {
142
+ rowEl.classList.add('collapsed');
143
+ rowEl.classList.remove('expanded');
144
+ e.target.innerText = '+';
145
+ } else {
146
+ rowEl.classList.remove('collapsed');
147
+ rowEl.classList.add('expanded');
148
+ e.target.innerText = '-';
149
+ }
150
+ }
151
+
152
+ }
153
+
154
+ if (!customElements.get('openapi-explorer')) {
155
+ customElements.define('schema-table', SchemaTable);
156
+ }