c95d8747e5b495d45ca8400a348fb4cb89e1bfa7
[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 { customEncodeURI } from "./url";
6
7 export class WebDAV {
8
9     private defaults: WebDAVDefaults = {
10         baseURL: '',
11         headers: {
12             'Cache-Control': 'must-revalidate'
13         },
14     };
15
16     constructor(config?: Partial<WebDAVDefaults>, private createRequest = () => new XMLHttpRequest()) {
17         if (config) {
18             this.defaults = {
19                 ...this.defaults,
20                 ...config,
21                 headers: {
22                     ...this.defaults.headers,
23                     ...config.headers
24                 },
25             };
26         }
27     }
28
29     getBaseUrl = (): string => this.defaults.baseURL;
30     setAuthorization = (token?) => this.defaults.headers.Authorization = token;
31
32     propfind = (url: string, config: WebDAVRequestConfig = {}) =>
33         this.request({
34             ...config, url,
35             method: 'PROPFIND'
36         })
37
38     put = (url: string, data?: any, config: WebDAVRequestConfig = {}) =>
39         this.request({
40             ...config, url,
41             method: 'PUT',
42             data
43         })
44
45     get = (url: string, config: WebDAVRequestConfig = {}) =>
46         this.request({
47             ...config, url,
48             method: 'GET'
49         })
50
51     upload = (url: string, files: File[], config: WebDAVRequestConfig = {}) => {
52         return Promise.all(
53             files.map(file => this.request({
54                 ...config, url,
55                 method: 'PUT',
56                 data: file
57             }))
58         );
59     }
60
61     copy = (url: string, destination: string, config: WebDAVRequestConfig = {}) =>
62         this.request({
63             ...config, url,
64             method: 'COPY',
65             headers: {
66                 ...config.headers,
67                 Destination: this.defaults.baseURL
68                     ? this.defaults.baseURL.replace(/\/+$/, '') + '/' + destination.replace(/^\/+/, '')
69                     : destination
70             }
71         })
72
73     move = (url: string, destination: string, config: WebDAVRequestConfig = {}) =>
74         this.request({
75             ...config, url,
76             method: 'MOVE',
77             headers: {
78                 ...config.headers,
79                 Destination: this.defaults.baseURL
80                     ? this.defaults.baseURL.replace(/\/+$/, '') + '/' + destination.replace(/^\/+/, '')
81                     : destination
82             }
83         })
84
85     delete = (url: string, config: WebDAVRequestConfig = {}) =>
86         this.request({
87             ...config, url,
88             method: 'DELETE'
89         })
90
91     mkdir = (url: string, config: WebDAVRequestConfig = {}) =>
92         this.request({
93             ...config, url,
94             method: 'MKCOL',
95             headers: {
96                 ...config.headers,
97             }
98         })
99
100     private request = (config: RequestConfig) => {
101         return new Promise<XMLHttpRequest>((resolve, reject) => {
102             const r = this.createRequest();
103             this.defaults.baseURL = this.defaults.baseURL.replace(/\/+$/, '');
104             r.open(config.method,
105                 `${this.defaults.baseURL
106                     ? this.defaults.baseURL+'/'
107                     : ''}${customEncodeURI(config.url)}`);
108
109             const headers = { ...this.defaults.headers, ...config.headers };
110             Object
111                 .keys(headers)
112                 .forEach(key => r.setRequestHeader(key, headers[key]));
113
114             if (!(window as any).cancelTokens) {
115                 Object.assign(window, { cancelTokens: {} });
116             }
117
118             (window as any).cancelTokens[config.url] = () => {
119                 resolve(r);
120                 r.abort();
121             }
122
123             if (config.onUploadProgress) {
124                 r.upload.addEventListener('progress', config.onUploadProgress);
125             }
126
127             // This event gets triggered on *any* server response
128             r.addEventListener('load', () => {
129                 if (r.status >= 400) {
130                     return reject(r);
131                 } else {
132                     return resolve(r);
133                 }
134             });
135
136             // This event gets triggered on network errors
137             r.addEventListener('error', () => {
138                 return reject(r);
139             });
140
141             r.upload.addEventListener('error', () => {
142                 return reject(r);
143             });
144
145             r.send(config.data);
146         });
147     }
148 }
149
150 export interface WebDAVRequestConfig {
151     headers?: {
152         [key: string]: string;
153     };
154     onUploadProgress?: (event: ProgressEvent) => void;
155 }
156
157 interface WebDAVDefaults {
158     baseURL: string;
159     headers: { [key: string]: string };
160 }
161
162 interface RequestConfig {
163     method: string;
164     url: string;
165     headers?: { [key: string]: string };
166     data?: any;
167     onUploadProgress?: (event: ProgressEvent) => void;
168 }