Merge branch '17018-readonly-file-actions-fix'
[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                     : ''}${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             r.addEventListener('load', () => {
89                 if (r.status === 404) {
90                     return reject(r);
91                 } else {
92                     return resolve(r);
93                 }
94             });
95
96             r.addEventListener('error', () => {
97                 return reject(r);
98             });
99
100             r.upload.addEventListener('error', () => {
101                 return reject(r);
102             });
103
104             r.send(config.data);
105         });
106     }
107 }
108
109 export interface WebDAVRequestConfig {
110     headers?: {
111         [key: string]: string;
112     };
113     onUploadProgress?: (event: ProgressEvent) => void;
114 }
115
116 interface WebDAVDefaults {
117     baseURL: string;
118     headers: { [key: string]: string };
119 }
120
121 interface RequestConfig {
122     method: string;
123     url: string;
124     headers?: { [key: string]: string };
125     data?: any;
126     onUploadProgress?: (event: ProgressEvent) => void;
127 }