17426: Add dialog box with form to example plugin.
[arvados-workbench2.git] / src / common / webdav.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 export class WebDAV {
6
7     defaults: WebDAVDefaults = {
8         baseURL: '',
9         headers: {},
10     };
11
12     constructor(config?: Partial<WebDAVDefaults>, private createRequest = () => new XMLHttpRequest()) {
13         if (config) {
14             this.defaults = { ...this.defaults, ...config };
15         }
16     }
17
18     propfind = (url: string, config: WebDAVRequestConfig = {}) =>
19         this.request({
20             ...config, url,
21             method: 'PROPFIND'
22         })
23
24     put = (url: string, data?: any, config: WebDAVRequestConfig = {}) =>
25         this.request({
26             ...config, url,
27             method: 'PUT',
28             data
29         })
30
31     upload = (url: string, files: File[], config: WebDAVRequestConfig = {}) => {
32         return Promise.all(
33             files.map(file => this.request({
34                 ...config, url,
35                 method: 'PUT',
36                 data: file
37             }))
38         );
39     }
40
41     copy = (url: string, destination: string, config: WebDAVRequestConfig = {}) =>
42         this.request({
43             ...config, url,
44             method: 'COPY',
45             headers: {
46                 ...config.headers,
47                 Destination: this.defaults.baseURL
48                     ? this.defaults.baseURL.replace(/\/+$/, '') + '/' + destination.replace(/^\/+/, '')
49                     : destination
50             }
51         })
52
53     move = (url: string, destination: string, config: WebDAVRequestConfig = {}) =>
54         this.request({
55             ...config, url,
56             method: 'MOVE',
57             headers: {
58                 ...config.headers,
59                 Destination: this.defaults.baseURL
60                     ? this.defaults.baseURL.replace(/\/+$/, '') + '/' + destination.replace(/^\/+/, '')
61                     : destination
62             }
63         })
64
65     delete = (url: string, config: WebDAVRequestConfig = {}) =>
66         this.request({
67             ...config, url,
68             method: 'DELETE'
69         })
70
71     private request = (config: RequestConfig) => {
72         return new Promise<XMLHttpRequest>((resolve, reject) => {
73             const r = this.createRequest();
74             this.defaults.baseURL = this.defaults.baseURL.replace(/\/+$/, '');
75             r.open(config.method,
76                 `${this.defaults.baseURL
77                     ? this.defaults.baseURL+'/'
78                     : ''}${encodeURI(config.url)}`);
79             const headers = { ...this.defaults.headers, ...config.headers };
80             Object
81                 .keys(headers)
82                 .forEach(key => r.setRequestHeader(key, headers[key]));
83
84             if (config.onUploadProgress) {
85                 r.upload.addEventListener('progress', config.onUploadProgress);
86             }
87
88             // This event gets triggered on *any* server response
89             r.addEventListener('load', () => {
90                 if (r.status >= 400) {
91                     return reject(r);
92                 } else {
93                     return resolve(r);
94                 }
95             });
96
97             // This event gets triggered on network errors
98             r.addEventListener('error', () => {
99                 return reject(r);
100             });
101
102             r.upload.addEventListener('error', () => {
103                 return reject(r);
104             });
105
106             r.send(config.data);
107         });
108     }
109 }
110
111 export interface WebDAVRequestConfig {
112     headers?: {
113         [key: string]: string;
114     };
115     onUploadProgress?: (event: ProgressEvent) => void;
116 }
117
118 interface WebDAVDefaults {
119     baseURL: string;
120     headers: { [key: string]: string };
121 }
122
123 interface RequestConfig {
124     method: string;
125     url: string;
126     headers?: { [key: string]: string };
127     data?: any;
128     onUploadProgress?: (event: ProgressEvent) => void;
129 }