@api-client/ui 0.0.12 → 0.0.14

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 (194) hide show
  1. package/demo/elements/authorization/cc.ts +56 -27
  2. package/dist/bindings/base/FileBindings.d.ts +4 -0
  3. package/dist/bindings/base/FileBindings.d.ts.map +1 -1
  4. package/dist/bindings/base/FileBindings.js +21 -1
  5. package/dist/bindings/base/FileBindings.js.map +1 -1
  6. package/dist/bindings/base/StoreBindings.d.ts +1 -17
  7. package/dist/bindings/base/StoreBindings.d.ts.map +1 -1
  8. package/dist/bindings/base/StoreBindings.js +0 -59
  9. package/dist/bindings/base/StoreBindings.js.map +1 -1
  10. package/dist/bindings/web/WebFileBindings.js +1 -1
  11. package/dist/bindings/web/WebFileBindings.js.map +1 -1
  12. package/dist/define/http/certificate-add.d.ts +9 -0
  13. package/dist/define/http/certificate-add.d.ts.map +1 -0
  14. package/dist/define/http/certificate-add.js +10 -0
  15. package/dist/define/http/certificate-add.js.map +1 -0
  16. package/dist/define/http/http-body-editor.d.ts.map +1 -1
  17. package/dist/define/http/http-body-editor.js +1 -1
  18. package/dist/define/http/http-body-editor.js.map +1 -1
  19. package/dist/define/ui/ui-segmented-button-set.d.ts +1 -1
  20. package/dist/define/ui/ui-segmented-button-set.d.ts.map +1 -1
  21. package/dist/define/ui/ui-segmented-button-set.js.map +1 -1
  22. package/dist/elements/authorization/ui/CC.styles.d.ts.map +1 -1
  23. package/dist/elements/authorization/ui/CC.styles.js +4 -9
  24. package/dist/elements/authorization/ui/CC.styles.js.map +1 -1
  25. package/dist/elements/authorization/ui/CcAuthorization.d.ts +14 -29
  26. package/dist/elements/authorization/ui/CcAuthorization.d.ts.map +1 -1
  27. package/dist/elements/authorization/ui/CcAuthorization.js +67 -158
  28. package/dist/elements/authorization/ui/CcAuthorization.js.map +1 -1
  29. package/dist/elements/http/BodyEditor.js +5 -5
  30. package/dist/elements/http/BodyEditor.js.map +1 -1
  31. package/dist/elements/http/BodyEditor.styles.d.ts +1 -1
  32. package/dist/elements/http/BodyEditor.styles.d.ts.map +1 -1
  33. package/dist/elements/http/BodyEditor.styles.js +5 -19
  34. package/dist/elements/http/BodyEditor.styles.js.map +1 -1
  35. package/dist/elements/http/CertificateAdd.element.d.ts +91 -0
  36. package/dist/elements/http/CertificateAdd.element.d.ts.map +1 -0
  37. package/dist/elements/http/CertificateAdd.element.js +389 -0
  38. package/dist/elements/http/CertificateAdd.element.js.map +1 -0
  39. package/dist/elements/http/CertificateAdd.styles.d.ts +3 -0
  40. package/dist/elements/http/CertificateAdd.styles.d.ts.map +1 -0
  41. package/dist/elements/http/CertificateAdd.styles.js +61 -0
  42. package/dist/elements/http/CertificateAdd.styles.js.map +1 -0
  43. package/dist/elements/project/ProjectRunReport.d.ts +2 -1
  44. package/dist/elements/project/ProjectRunReport.d.ts.map +1 -1
  45. package/dist/elements/project/ProjectRunReport.js.map +1 -1
  46. package/dist/elements/store/FilePicker.styles.d.ts.map +1 -1
  47. package/dist/elements/store/FilePicker.styles.js +1 -0
  48. package/dist/elements/store/FilePicker.styles.js.map +1 -1
  49. package/dist/elements/store/FilesLib.d.ts +2 -2
  50. package/dist/elements/store/FilesLib.d.ts.map +1 -1
  51. package/dist/elements/store/FilesLib.js.map +1 -1
  52. package/dist/events/EventTypes.d.ts +6 -7
  53. package/dist/events/EventTypes.d.ts.map +1 -1
  54. package/dist/events/EventTypes.js +7 -7
  55. package/dist/events/EventTypes.js.map +1 -1
  56. package/dist/events/Events.d.ts +6 -1
  57. package/dist/events/Events.d.ts.map +1 -1
  58. package/dist/events/Events.js +2 -0
  59. package/dist/events/Events.js.map +1 -1
  60. package/dist/events/FilesystemEvents.d.ts +8 -0
  61. package/dist/events/FilesystemEvents.d.ts.map +1 -0
  62. package/dist/events/FilesystemEvents.js +59 -0
  63. package/dist/events/FilesystemEvents.js.map +1 -0
  64. package/dist/events/HttpClientEvents.d.ts +0 -2
  65. package/dist/events/HttpClientEvents.d.ts.map +1 -1
  66. package/dist/events/HttpClientEvents.js +0 -2
  67. package/dist/events/HttpClientEvents.js.map +1 -1
  68. package/dist/http-client/idb/Arc18DataUpgrade.d.ts +0 -8
  69. package/dist/http-client/idb/Arc18DataUpgrade.d.ts.map +1 -1
  70. package/dist/http-client/idb/Arc18DataUpgrade.js +11 -206
  71. package/dist/http-client/idb/Arc18DataUpgrade.js.map +1 -1
  72. package/dist/http-client/store/Cursor.js +2 -2
  73. package/dist/http-client/store/Cursor.js.map +1 -1
  74. package/dist/http-client/store/HttpClientIdbDatabase.d.ts.map +1 -1
  75. package/dist/http-client/store/HttpClientIdbDatabase.js +2 -6
  76. package/dist/http-client/store/HttpClientIdbDatabase.js.map +1 -1
  77. package/dist/http-client/store/State.d.ts +1 -3
  78. package/dist/http-client/store/State.d.ts.map +1 -1
  79. package/dist/http-client/store/State.js.map +1 -1
  80. package/dist/http-client/store/StoreBroadcast.d.ts +0 -5
  81. package/dist/http-client/store/StoreBroadcast.d.ts.map +1 -1
  82. package/dist/http-client/store/StoreBroadcast.js +0 -7
  83. package/dist/http-client/store/StoreBroadcast.js.map +1 -1
  84. package/dist/http-client/store/idb/IdbStore.d.ts +1 -10
  85. package/dist/http-client/store/idb/IdbStore.d.ts.map +1 -1
  86. package/dist/http-client/store/idb/IdbStore.js +0 -48
  87. package/dist/http-client/store/idb/IdbStore.js.map +1 -1
  88. package/dist/http-client/store/types.d.ts +2 -56
  89. package/dist/http-client/store/types.d.ts.map +1 -1
  90. package/dist/http-client/store/types.js.map +1 -1
  91. package/dist/index.d.ts +1 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +2 -0
  94. package/dist/index.js.map +1 -1
  95. package/dist/lib/files/FileUtils.d.ts +9 -0
  96. package/dist/lib/files/FileUtils.d.ts.map +1 -0
  97. package/dist/lib/files/FileUtils.js +13 -0
  98. package/dist/lib/files/FileUtils.js.map +1 -0
  99. package/dist/mixins/RouteMixin.d.ts +4 -0
  100. package/dist/mixins/RouteMixin.d.ts.map +1 -1
  101. package/dist/mixins/RouteMixin.js +1 -0
  102. package/dist/mixins/RouteMixin.js.map +1 -1
  103. package/dist/pages/api-client/ApiClient.screen.d.ts +8 -4
  104. package/dist/pages/api-client/ApiClient.screen.d.ts.map +1 -1
  105. package/dist/pages/api-client/ApiClient.screen.js +25 -4
  106. package/dist/pages/api-client/ApiClient.screen.js.map +1 -1
  107. package/dist/pages/api-client/ApiClient.styles.d.ts.map +1 -1
  108. package/dist/pages/api-client/ApiClient.styles.js +0 -12
  109. package/dist/pages/api-client/ApiClient.styles.js.map +1 -1
  110. package/dist/pages/api-client/pages/Files.page.d.ts.map +1 -1
  111. package/dist/pages/api-client/pages/Files.page.js +13 -0
  112. package/dist/pages/api-client/pages/Files.page.js.map +1 -1
  113. package/dist/pages/api-client/pages/Trash.page.d.ts.map +1 -1
  114. package/dist/pages/api-client/pages/Trash.page.js +3 -11
  115. package/dist/pages/api-client/pages/Trash.page.js.map +1 -1
  116. package/dist/ui/button/SegmentedButtonsSet.d.ts +14 -0
  117. package/dist/ui/button/SegmentedButtonsSet.d.ts.map +1 -1
  118. package/dist/ui/button/SegmentedButtonsSet.js.map +1 -1
  119. package/dist/ui/list/UiDropdownList.js +1 -1
  120. package/dist/ui/list/UiDropdownList.js.map +1 -1
  121. package/dist/ui/notification/SnackNotifications.d.ts +1 -0
  122. package/dist/ui/notification/SnackNotifications.d.ts.map +1 -1
  123. package/dist/ui/notification/SnackNotifications.js +7 -0
  124. package/dist/ui/notification/SnackNotifications.js.map +1 -1
  125. package/package.json +2 -2
  126. package/src/bindings/base/FileBindings.ts +25 -1
  127. package/src/bindings/base/StoreBindings.ts +1 -73
  128. package/src/bindings/web/WebFileBindings.ts +1 -1
  129. package/src/define/http/certificate-add.ts +12 -0
  130. package/src/define/http/http-body-editor.ts +1 -1
  131. package/src/define/ui/ui-segmented-button-set.ts +1 -1
  132. package/src/elements/authorization/ui/CC.styles.ts +4 -9
  133. package/src/elements/authorization/ui/CcAuthorization.ts +67 -167
  134. package/src/elements/http/BodyEditor.styles.ts +5 -19
  135. package/src/elements/http/BodyEditor.ts +5 -5
  136. package/src/elements/http/CertificateAdd.element.ts +443 -0
  137. package/src/elements/http/CertificateAdd.styles.ts +61 -0
  138. package/src/elements/project/ProjectRunReport.ts +2 -1
  139. package/src/elements/store/FilePicker.styles.ts +1 -0
  140. package/src/elements/store/FilesLib.ts +2 -2
  141. package/src/events/EventTypes.ts +7 -7
  142. package/src/events/Events.ts +2 -0
  143. package/src/events/FilesystemEvents.ts +63 -0
  144. package/src/events/HttpClientEvents.ts +0 -2
  145. package/src/http-client/idb/Arc18DataUpgrade.ts +84 -84
  146. package/src/http-client/store/Cursor.ts +2 -2
  147. package/src/http-client/store/HttpClientIdbDatabase.ts +5 -9
  148. package/src/http-client/store/State.ts +1 -5
  149. package/src/http-client/store/StoreBroadcast.ts +0 -8
  150. package/src/http-client/store/idb/IdbStore.ts +1 -51
  151. package/src/http-client/store/types.ts +2 -57
  152. package/src/index.ts +3 -0
  153. package/src/lib/files/FileUtils.ts +12 -0
  154. package/src/mixins/RouteMixin.ts +8 -1
  155. package/src/pages/api-client/ApiClient.screen.ts +33 -9
  156. package/src/pages/api-client/ApiClient.styles.ts +0 -12
  157. package/src/pages/api-client/pages/Files.page.ts +11 -0
  158. package/src/pages/api-client/pages/Trash.page.ts +3 -10
  159. package/src/ui/button/SegmentedButtonsSet.ts +16 -1
  160. package/src/ui/list/UiDropdownList.ts +1 -1
  161. package/src/ui/notification/SnackNotifications.ts +8 -0
  162. package/test/elements/http/BodyFormdataEditorElement.test.ts +458 -454
  163. package/test/elements/http/BodyMultipartEditorElement.test.ts +609 -605
  164. package/test/elements/http/BodyRawEditorElement.test.ts +60 -56
  165. package/test/elements/http/CertificateAdd.test.ts +430 -0
  166. package/test/events/EventTypes.test.ts +0 -22
  167. package/test/helpers/UiMock.ts +19 -2
  168. package/web-test-runner.config.mjs +6 -5
  169. package/dist/events/http-client/models/CertificatesEvents.d.ts +0 -12
  170. package/dist/events/http-client/models/CertificatesEvents.d.ts.map +0 -1
  171. package/dist/events/http-client/models/CertificatesEvents.js +0 -18
  172. package/dist/events/http-client/models/CertificatesEvents.js.map +0 -1
  173. package/dist/http-client/idb/AuthDataModel.d.ts +0 -60
  174. package/dist/http-client/idb/AuthDataModel.d.ts.map +0 -1
  175. package/dist/http-client/idb/AuthDataModel.js +0 -150
  176. package/dist/http-client/idb/AuthDataModel.js.map +0 -1
  177. package/dist/http-client/idb/HostsModel.d.ts +0 -25
  178. package/dist/http-client/idb/HostsModel.d.ts.map +0 -1
  179. package/dist/http-client/idb/HostsModel.js +0 -82
  180. package/dist/http-client/idb/HostsModel.js.map +0 -1
  181. package/dist/http-client/idb/LegacyMockedStore.d.ts +0 -214
  182. package/dist/http-client/idb/LegacyMockedStore.d.ts.map +0 -1
  183. package/dist/http-client/idb/LegacyMockedStore.js +0 -486
  184. package/dist/http-client/idb/LegacyMockedStore.js.map +0 -1
  185. package/dist/http-client/store/Validator.d.ts +0 -4
  186. package/dist/http-client/store/Validator.d.ts.map +0 -1
  187. package/dist/http-client/store/Validator.js +0 -32
  188. package/dist/http-client/store/Validator.js.map +0 -1
  189. package/src/events/http-client/models/CertificatesEvents.ts +0 -23
  190. package/src/http-client/idb/AuthDataModel.ts +0 -175
  191. package/src/http-client/idb/HostsModel.ts +0 -125
  192. package/src/http-client/idb/LegacyMockedStore.ts +0 -544
  193. package/src/http-client/store/Validator.ts +0 -33
  194. package/test/apic-ui.test.ts +0 -31
@@ -0,0 +1,443 @@
1
+ import { Certificate, CertificateFile, CertificateType } from "@api-client/core/build/browser.js";
2
+ import { CSSResult, html, nothing, TemplateResult } from "lit";
3
+ import { property, state } from "lit/decorators.js";
4
+ import { live } from "lit/directives/live.js";
5
+ import { Events } from "../../events/Events.js";
6
+ import ApiElement from "../ApiElement.js";
7
+ import styles from './CertificateAdd.styles.js';
8
+ import { FileUtils } from "../../lib/files/FileUtils.js";
9
+ import type { ISegmentedButtonSelectionDetail } from '../../ui/button/SegmentedButtonsSet.js';
10
+ import type SegmentedButton from "../../ui/button/SegmentedButton.js";
11
+ import type Input from "../../ui/input/Input.js";
12
+ import type { IFileFilter, IOpenFileDialogInit } from "../../bindings/base/FileBindings.js";
13
+ import { SnackNotifications } from "../../ui/notification/SnackNotifications.js";
14
+ import '../../define/ui/ui-segmented-button.js';
15
+ import '../../define/ui/ui-segmented-button-set.js';
16
+ import '../../define/ui/ui-text-field.js';
17
+ import '../../define/ui/ui-button.js';
18
+ import '../../define/ui/ui-icon.js';
19
+ import '../../define/ui/ui-progress.js';
20
+
21
+ interface CertificateInfo {
22
+ fileName: string;
23
+ contents: ArrayBuffer | Buffer;
24
+ }
25
+
26
+ /**
27
+ * An element that renders a certificate import UI.
28
+ *
29
+ * Notes for implementation:
30
+ * - `p12` is a single file, may have a passphrase
31
+ * - `pem` is 2 files: the certificate and the key
32
+ * - `pem` may have a `passphrase` on the key but not on the certificate.
33
+ */
34
+ export default class CertificateAdd extends ApiElement {
35
+ static override get styles(): CSSResult[] {
36
+ return styles;
37
+ }
38
+
39
+ /**
40
+ * The type of the certificate to import.
41
+ * @attribute
42
+ */
43
+ @property({ type: String }) importType: CertificateType = 'pem';
44
+
45
+ /**
46
+ * The parent folder where to insert the imported certificate.
47
+ * @attribute
48
+ */
49
+ @property({ type: String }) folder?: string;
50
+
51
+ /**
52
+ * The current workspace for the file.
53
+ * @attribute
54
+ */
55
+ @property({ type: String }) space?: string;
56
+
57
+ /**
58
+ * When an IO operation is in progress.
59
+ */
60
+ @state() working?: boolean;
61
+
62
+ @state() certificateFile?: CertificateInfo;
63
+
64
+ @state() keyFile?: CertificateInfo;
65
+
66
+ /**
67
+ * The password for the certificate
68
+ */
69
+ certificatePassword?: string;
70
+
71
+ /**
72
+ * The password for the key.
73
+ */
74
+ keyPassword?: string;
75
+
76
+ /**
77
+ * The user entered certificate name.
78
+ */
79
+ protected certificateName?: string;
80
+
81
+ get hasKeyImport(): boolean {
82
+ return this.importType === 'pem';
83
+ }
84
+
85
+ get hasCertificatePasswordInput(): boolean {
86
+ return this.importType === 'p12';
87
+ }
88
+
89
+ protected handleViewSelection(e: CustomEvent<ISegmentedButtonSelectionDetail>): void {
90
+ if (!e.detail.selected) {
91
+ return;
92
+ }
93
+ const button = e.detail.button as SegmentedButton;
94
+ const type = button.dataset.value as CertificateType | undefined;
95
+ if (type) {
96
+ this.importType = type;
97
+ }
98
+ }
99
+
100
+ protected handleNameChange(e: Event): void {
101
+ const input = e.target as Input;
102
+ this.certificateName = input.value;
103
+ }
104
+
105
+ protected handleCertificatePasswordChange(e: Event): void {
106
+ const input = e.target as Input;
107
+ this.certificatePassword = input.value;
108
+ }
109
+
110
+ protected handleKeyPasswordChange(e: Event): void {
111
+ const input = e.target as Input;
112
+ this.keyPassword = input.value;
113
+ }
114
+
115
+ protected handleSelectCertificate(): void {
116
+ this.selectCertificateFLow();
117
+ }
118
+
119
+ protected handleSelectKey(): void {
120
+ this.selectKeyFLow();
121
+ }
122
+
123
+ async selectCertificateFLow(): Promise<void> {
124
+ this.working = true;
125
+ const options: IOpenFileDialogInit = {
126
+ multiple: false,
127
+ title: 'Select a certificate file',
128
+ filters: [{
129
+ description: 'Certificate files',
130
+ accept: {},
131
+ }],
132
+ };
133
+ const filters = options.filters as IFileFilter[];
134
+ if (this.importType === 'p12') {
135
+ filters[0].accept = {
136
+ 'application/x-pkcs12': ['.p12', '.pfx'],
137
+ };
138
+ } else {
139
+ filters[0].accept = {
140
+ 'application/x-pem-file': ['.pem', '.crt', '.cer', '.key'],
141
+ };
142
+ }
143
+ const pickResult = await Events.Filesystem.requestOpenFile(options, this);
144
+ if (!pickResult || pickResult.canceled) {
145
+ this.working = false;
146
+ return;
147
+ }
148
+ const [path] = pickResult.filePath as string[];
149
+ const readResult = await Events.Filesystem.readFile(path, { returnType: 'buffer', dispose: true }, this);
150
+ if (!readResult) {
151
+ this.working = false;
152
+ return;
153
+ }
154
+
155
+ const fileName = FileUtils.getFileName(path);
156
+ this.certificateFile = {
157
+ fileName,
158
+ contents: readResult as ArrayBuffer | Buffer,
159
+ };
160
+ this.working = false;
161
+ }
162
+
163
+ async selectKeyFLow(): Promise<void> {
164
+ this.working = true;
165
+ const options: IOpenFileDialogInit = {
166
+ multiple: false,
167
+ title: 'Select a key file',
168
+ filters: [{
169
+ description: 'Certificate key files',
170
+ accept: {
171
+ 'application/x-pem-file': ['.pem', '.crt', '.cer', '.key']
172
+ },
173
+ }],
174
+ };
175
+ const pickResult = await Events.Filesystem.requestOpenFile(options, this);
176
+ if (!pickResult || pickResult.canceled) {
177
+ this.working = false;
178
+ return;
179
+ }
180
+ const [path] = pickResult.filePath as string[];
181
+ const readResult = await Events.Filesystem.readFile(path, { returnType: 'buffer', dispose: true }, this);
182
+ if (!readResult) {
183
+ this.working = false;
184
+ return;
185
+ }
186
+
187
+ const fileName = FileUtils.getFileName(path);
188
+ this.keyFile = {
189
+ fileName,
190
+ contents: readResult as ArrayBuffer | Buffer,
191
+ };
192
+ this.working = false;
193
+ }
194
+
195
+ protected isValidDropTarget(dt: DataTransfer): boolean {
196
+ const types = Array.from(dt.types);
197
+ return types.includes('Files');
198
+ }
199
+
200
+ protected handleFileDragEnter(e: DragEvent): void {
201
+ const dt = e.dataTransfer as DataTransfer;
202
+ if (!this.isValidDropTarget(dt)) {
203
+ return;
204
+ }
205
+ e.preventDefault();
206
+ dt.dropEffect = 'copy';
207
+ const node = e.currentTarget as HTMLElement;
208
+ node.classList.add('dragover');
209
+ }
210
+
211
+ protected handleFileDragLeave(e: DragEvent): void {
212
+ const dt = e.dataTransfer as DataTransfer;
213
+ if (!this.isValidDropTarget(dt)) {
214
+ return;
215
+ }
216
+ e.preventDefault();
217
+ dt.dropEffect = 'none';
218
+ const node = e.currentTarget as HTMLElement;
219
+ node.classList.remove('dragover');
220
+ }
221
+
222
+ protected handleFileDragOver(e: DragEvent): void {
223
+ const dt = e.dataTransfer as DataTransfer;
224
+ if (!this.isValidDropTarget(dt)) {
225
+ return;
226
+ }
227
+ e.preventDefault();
228
+ const node = e.currentTarget as HTMLElement;
229
+ node.classList.add('dragover');
230
+ }
231
+
232
+ protected handleFileDrop(e: DragEvent): void {
233
+ const dt = e.dataTransfer as DataTransfer;
234
+ e.preventDefault();
235
+ const node = e.currentTarget as HTMLElement;
236
+ node.classList.remove('dragover');
237
+ const file = dt.files[0];
238
+ if (!file) {
239
+ return;
240
+ }
241
+ const { kind } = node.dataset;
242
+ if (!kind) {
243
+ return;
244
+ }
245
+ this.processDroppedFile(file, kind as 'certificate' | 'key');
246
+ }
247
+
248
+ async processDroppedFile(file: File, kind: 'certificate' | 'key'): Promise<void> {
249
+ this.working = true;
250
+ const buff = await file.arrayBuffer();
251
+ const fileName = FileUtils.getFileName(file.name);
252
+ if (kind === 'certificate') {
253
+ this.certificateFile = {
254
+ fileName,
255
+ contents: buff,
256
+ };
257
+ } else if (kind === 'key') {
258
+ this.keyFile = {
259
+ fileName,
260
+ contents: buff,
261
+ };
262
+ }
263
+ this.working = false;
264
+ }
265
+
266
+ protected handleCancel(): void {
267
+ this.dispatchEvent(new Event('close'));
268
+ }
269
+
270
+ protected handleImport(): void {
271
+ this.submit();
272
+ }
273
+
274
+ async submit(): Promise<void> {
275
+ const invalid = this.reportValidity();
276
+ if (invalid) {
277
+ return;
278
+ }
279
+ const { certificateFile, certificateName, certificatePassword, keyFile, keyPassword, space, folder } = this;
280
+ if (!space) {
281
+ throw new Error(`The workspace key is not set on the certificate import element.`);
282
+ }
283
+ const cert = this.importType === 'p12' ?
284
+ Certificate.fromP12(certificateFile?.contents as ArrayBuffer, certificateName as string, certificatePassword) :
285
+ Certificate.fromPem(certificateFile?.contents as ArrayBuffer, keyFile?.contents as ArrayBuffer, certificateName as string, keyPassword);
286
+ const file = CertificateFile.fromCertificate(cert, space);
287
+ await Events.Store.File.create(file.toJSON(), cert.toJSON(), { parent: folder }, this);
288
+ this.handleCancel();
289
+ }
290
+
291
+ reportValidity(): boolean {
292
+ const inputs = Array.from((this.shadowRoot as ShadowRoot).querySelectorAll('ui-text-field'));
293
+ let invalid = false;
294
+ inputs.forEach((input) => {
295
+ const inputResult = input.reportValidity();
296
+ if (!inputResult) {
297
+ invalid = true;
298
+ }
299
+ });
300
+
301
+ if (!invalid) {
302
+ if (!this.certificateFile) {
303
+ invalid = true;
304
+ SnackNotifications.notify(`Certificate file is required.`);
305
+ }
306
+ if (!invalid && this.hasKeyImport && !this.keyFile) {
307
+ invalid = true;
308
+ SnackNotifications.notify(`The key file is required.`);
309
+ }
310
+ }
311
+ return invalid;
312
+ }
313
+
314
+ protected override render(): TemplateResult {
315
+ return html`
316
+ <div class="form">
317
+ ${this.renderProgress()}
318
+ ${this.renderViewToggle()}
319
+ ${this.renderNameInput()}
320
+ ${this.renderCertificateFile()}
321
+ ${this.renderCertificatePasswordInput()}
322
+ ${this.renderKeyFile()}
323
+ ${this.renderKeyPasswordInput()}
324
+ ${this.renderImportButton()}
325
+ </div>
326
+ `;
327
+ }
328
+
329
+ protected renderViewToggle(): TemplateResult {
330
+ const { importType } = this;
331
+ return html`
332
+ <div class="view-toggle">
333
+ <ui-segmented-button-set @select="${this.handleViewSelection}">
334
+ <ui-segmented-button
335
+ .selected="${importType === 'pem'}"
336
+ title="Import a certificate in PEM format"
337
+ data-value="pem"
338
+ >PEM</ui-segmented-button>
339
+ <ui-segmented-button
340
+ .selected="${importType === 'p12'}"
341
+ title="Import a bundled PFX or PKCS12 encoded private key and certificate"
342
+ data-value="p12"
343
+ >PFX or PKCS12</ui-segmented-button>
344
+ </ui-segmented-button-set>
345
+ </div>
346
+ `;
347
+ }
348
+
349
+ protected renderNameInput(): TemplateResult {
350
+ const { certificateName = '' } = this;
351
+ return html`
352
+ <div class="form-input">
353
+ <ui-text-field
354
+ required
355
+ name="name"
356
+ .value="${live(certificateName)}"
357
+ label="Certificate name"
358
+ @change="${this.handleNameChange}"
359
+ invalidText="The name is required"
360
+ ></ui-text-field>
361
+ </div>
362
+ `;
363
+ }
364
+
365
+ protected renderCertificateFile(): TemplateResult {
366
+ const { certificateFile } = this;
367
+ return html`
368
+ <div class="cert-file" data-kind="certificate" @dragenter="${this.handleFileDragEnter}" @dragleave="${this.handleFileDragLeave}" @dragover="${this.handleFileDragOver}" @drop="${this.handleFileDrop}">
369
+ <p class="body-medium">Select or drop a certificate file here.</p>
370
+ <ui-button type="tonal" @click="${this.handleSelectCertificate}">Select certificate file</ui-button>
371
+ ${certificateFile ? html`<span class="cert-info body-medium">${certificateFile.fileName}</span>` : html`<span class="missing body-medium"><ui-icon role="presentation" icon="warning" class="missing-icon"></ui-icon>Certificate is not selected</span>`}
372
+ </div>
373
+ `;
374
+ }
375
+
376
+ protected renderCertificatePasswordInput(): TemplateResult | typeof nothing {
377
+ const { certificatePassword = '' } = this;
378
+ if (!this.hasCertificatePasswordInput) {
379
+ return nothing;
380
+ }
381
+ return html`
382
+ <div class="form-input">
383
+ <ui-text-field
384
+ name="certificatePassword"
385
+ type="password"
386
+ .value="${live(certificatePassword)}"
387
+ label="Certificate password"
388
+ @change="${this.handleCertificatePasswordChange}"
389
+ supportingText="Optional password for the certificate"
390
+ ></ui-text-field>
391
+ </div>
392
+ `;
393
+ }
394
+
395
+ protected renderKeyFile(): TemplateResult | typeof nothing {
396
+ if (!this.hasKeyImport) {
397
+ return nothing;
398
+ }
399
+ const { keyFile } = this;
400
+ return html`
401
+ <div class="cert-file" data-kind="key" @dragenter="${this.handleFileDragEnter}" @dragleave="${this.handleFileDragLeave}" @dragover="${this.handleFileDragOver}" @drop="${this.handleFileDrop}">
402
+ <p class="body-medium">Select or drop a key file here.</p>
403
+ <ui-button type="tonal" @click="${this.handleSelectKey}">Select key file</ui-button>
404
+ ${keyFile ? html`<span class="cert-info body-medium">${keyFile.fileName}</span>` : html`<span class="missing body-medium"><ui-icon role="presentation" icon="warning" class="missing-icon"></ui-icon>Key is not selected</span>`}
405
+ </div>
406
+ `;
407
+ }
408
+
409
+ protected renderKeyPasswordInput(): TemplateResult | typeof nothing {
410
+ if (!this.hasKeyImport) {
411
+ return nothing;
412
+ }
413
+ const { keyPassword = '' } = this;
414
+ return html`
415
+ <div class="form-input">
416
+ <ui-text-field
417
+ name="keyPassword"
418
+ type="password"
419
+ .value="${live(keyPassword)}"
420
+ label="Key password"
421
+ @change="${this.handleKeyPasswordChange}"
422
+ supportingText="Optional password for the key"
423
+ ></ui-text-field>
424
+ </div>
425
+ `;
426
+ }
427
+
428
+ protected renderImportButton(): TemplateResult {
429
+ return html`
430
+ <div class="form-action">
431
+ <ui-button type="filled" @click="${this.handleImport}">Import</ui-button>
432
+ <ui-button type="text" @click="${this.handleCancel}">Cancel</ui-button>
433
+ </div>
434
+ `;
435
+ }
436
+
437
+ protected renderProgress(): TemplateResult | typeof nothing {
438
+ if (!this.working) {
439
+ return nothing;
440
+ }
441
+ return html`<ui-progress indeterminate class="working"></ui-progress>`;
442
+ }
443
+ }
@@ -0,0 +1,61 @@
1
+ import { css } from "lit";
2
+ import typography from '../../styles/m3/typography.module.js';
3
+
4
+ export default [typography, css`
5
+ :host {
6
+ display: block;
7
+ }
8
+
9
+ .form {
10
+ position: relative;
11
+ }
12
+
13
+ .working {
14
+ position: absolute;
15
+ top: 1px;
16
+ width: 100%;
17
+ }
18
+
19
+ .view-toggle {
20
+ margin-bottom: 20px;
21
+ padding-top: 20px;
22
+ }
23
+
24
+ .form-input {
25
+ margin: 12px 0;
26
+ }
27
+
28
+ .cert-file {
29
+ margin: 20px 0;
30
+ padding: 8px 4px;
31
+ border: 1px transparent solid;
32
+ }
33
+
34
+ .cert-file.dragover {
35
+ background-color: var(--md-sys-color-surface);
36
+ border-color: var(--md-sys-color-outline-variant);
37
+ border-radius: 8px;
38
+ }
39
+
40
+ .missing,
41
+ .cert-info {
42
+ display: inline-flex;
43
+ margin-left: 12px;
44
+ vertical-align: middle;
45
+ }
46
+
47
+ .missing-icon {
48
+ fill: var(--md-sys-color-error);
49
+ width: 16px;
50
+ height: 16px;
51
+ margin-right: 8px;
52
+ }
53
+
54
+ ui-text-field {
55
+ min-width: 320px;
56
+ }
57
+
58
+ .form-action {
59
+ margin-top: 20px;
60
+ }
61
+ `];
@@ -8,6 +8,7 @@ import {
8
8
  import { statusTemplate } from '../http/HttpStatus.js';
9
9
  import SegmentedButton from '../../ui/button/SegmentedButton.js';
10
10
  import { RequestLogView } from '../http/RequestLog.js';
11
+ import type { ISegmentedButtonSelectionDetail } from '../../ui/button/SegmentedButtonsSet.js';
11
12
  import '@github/relative-time-element';
12
13
  import '../../define/http/http-request-log.js';
13
14
  import '../../define/ui/ui-icon.js';
@@ -125,7 +126,7 @@ export default class ProjectRunReport extends LitElement {
125
126
  this.currentLog = item;
126
127
  }
127
128
 
128
- protected handleViewSelection(e: CustomEvent): void {
129
+ protected handleViewSelection(e: CustomEvent<ISegmentedButtonSelectionDetail>): void {
129
130
  if (!e.detail.selected) {
130
131
  return;
131
132
  }
@@ -68,5 +68,6 @@ export default [typography, css`
68
68
 
69
69
  .no-files {
70
70
  flex: 1;
71
+ margin-left: 16px;
71
72
  }
72
73
  `];
@@ -1,4 +1,4 @@
1
- import { CertificateFileKind, DataFileKind, FolderKind, IFile, ProjectKind } from "@api-client/core/build/browser.js";
1
+ import { CertificateFileKind, DataFileKind, FolderKind, IFile, ProjectKind, TrashEntry } from "@api-client/core/build/browser.js";
2
2
  import { IconType } from "../../ui/icons/Icons.js";
3
3
 
4
4
  /**
@@ -19,7 +19,7 @@ export function filesSortFunction(a: IFile, b: IFile): number {
19
19
  return b.lastModified.time - a.lastModified.time;
20
20
  }
21
21
 
22
- export function fileIcon(item: IFile): IconType | undefined {
22
+ export function fileIcon(item: IFile | TrashEntry): IconType | undefined {
23
23
  let icon: IconType | undefined;
24
24
  switch (item.kind) {
25
25
  case DataFileKind: icon = 'schema'; break;
@@ -49,13 +49,6 @@ export const EventTypes = Object.freeze({
49
49
  clearProject: 'storeprojectrunsclearproject',
50
50
  clearParent: 'storeprojectrunsclearparent',
51
51
  }),
52
- Certificate: Object.freeze({
53
- read: 'storeclientcertificateread',
54
- list: 'storeclientcertificatelist',
55
- delete: 'storeclientcertificatedelete',
56
- update: 'storeclientcertificateupdate',
57
- insert: 'storeclientcertificateinsert',
58
- }),
59
52
  }),
60
53
  Config: Object.freeze({
61
54
  Environment: Object.freeze({
@@ -182,4 +175,11 @@ export const EventTypes = Object.freeze({
182
175
  }),
183
176
  }),
184
177
  }),
178
+ // Filesystem access
179
+ Filesystem: Object.freeze({
180
+ requestSaveFile: 'filesystemrequestsavefile',
181
+ requestOpenFile: 'filesystemrequestopenfile',
182
+ writeFile: 'filesystemwritefile',
183
+ readFile: 'filesystemreadfile',
184
+ }),
185
185
  });
@@ -5,6 +5,7 @@ import { HttpProjectEvents } from './HttpProjectEvents.js';
5
5
  import { SchemaDesignEvents } from './SchemaDesignEvents.js';
6
6
  import { HttpClientEvents } from './HttpClientEvents.js';
7
7
  import { HttpEvents } from './HttpEvents.js';
8
+ import { FilesystemEvents } from './FilesystemEvents.js';
8
9
 
9
10
  export const Events = Object.freeze({
10
11
  Config: ConfigEvents,
@@ -14,4 +15,5 @@ export const Events = Object.freeze({
14
15
  HttpClient: HttpClientEvents,
15
16
  SchemaDesign: SchemaDesignEvents,
16
17
  Http: HttpEvents,
18
+ Filesystem: FilesystemEvents,
17
19
  });
@@ -0,0 +1,63 @@
1
+ import type { FileReadOptions, FileWriteOptions, IOpenFileDialogInit, IOpenFileDialogResult, ISaveFileDialogInit, ISaveFileDialogResult } from "../bindings/base/FileBindings.js";
2
+ import { EventTypes } from "./EventTypes.js";
3
+
4
+ export const FilesystemEvents = Object.freeze({
5
+ requestSaveFile: async (opts?: ISaveFileDialogInit, target: EventTarget = document.body): Promise<ISaveFileDialogResult | undefined> => {
6
+ const e = new CustomEvent(EventTypes.Filesystem.requestSaveFile, {
7
+ bubbles: true,
8
+ composed: true,
9
+ cancelable: true,
10
+ detail: {
11
+ options: opts,
12
+ result: undefined,
13
+ }
14
+ });
15
+ target.dispatchEvent(e);
16
+ return ((e.detail.result as unknown) as Promise<ISaveFileDialogResult>);
17
+ },
18
+
19
+ requestOpenFile: async (opts?: IOpenFileDialogInit, target: EventTarget = document.body): Promise<IOpenFileDialogResult | undefined> => {
20
+ const e = new CustomEvent(EventTypes.Filesystem.requestOpenFile, {
21
+ bubbles: true,
22
+ composed: true,
23
+ cancelable: true,
24
+ detail: {
25
+ options: opts,
26
+ result: undefined,
27
+ }
28
+ });
29
+ target.dispatchEvent(e);
30
+ return ((e.detail.result as unknown) as Promise<IOpenFileDialogResult>);
31
+ },
32
+
33
+ writeFile: async (path: string, contents: BufferSource | Blob | string | Buffer, opts?: FileWriteOptions, target: EventTarget = document.body): Promise<void> => {
34
+ const e = new CustomEvent(EventTypes.Filesystem.writeFile, {
35
+ bubbles: true,
36
+ composed: true,
37
+ cancelable: true,
38
+ detail: {
39
+ path,
40
+ contents,
41
+ options: opts,
42
+ result: undefined,
43
+ }
44
+ });
45
+ target.dispatchEvent(e);
46
+ await ((e.detail.result as unknown) as Promise<void>);
47
+ },
48
+
49
+ readFile: async (path: string, opts?: FileReadOptions, target: EventTarget = document.body): Promise<Buffer | ArrayBuffer | string | undefined> => {
50
+ const e = new CustomEvent(EventTypes.Filesystem.readFile, {
51
+ bubbles: true,
52
+ composed: true,
53
+ cancelable: true,
54
+ detail: {
55
+ path,
56
+ options: opts,
57
+ result: undefined,
58
+ }
59
+ });
60
+ target.dispatchEvent(e);
61
+ return ((e.detail.result as unknown) as Promise<Buffer | ArrayBuffer | string>);
62
+ },
63
+ });
@@ -1,6 +1,5 @@
1
1
  import { ContextDeleteEvent, ContextQueryEvent, IQueryResponse, IRequestUiMeta, IUrl } from '@api-client/core/build/browser.js';
2
2
  import { EventTypes } from './EventTypes.js';
3
- import { CertificatesEvents } from './http-client/models/CertificatesEvents.js';
4
3
 
5
4
  export interface IRequestUiInsertDetail {
6
5
  pid: string;
@@ -10,7 +9,6 @@ export interface IRequestUiInsertDetail {
10
9
 
11
10
  export const HttpClientEvents = Object.freeze({
12
11
  Model: Object.freeze({
13
- Certificate: CertificatesEvents,
14
12
  UrlHistory: Object.freeze({
15
13
  /**
16
14
  * Adds a new URL to the URL history store.