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