758a5e18e1e9ad44015b9a802d0e532e70ba3d76
[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     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                     : ''}${customEncodeURI(config.url)}`);
81
82             const headers = { ...this.defaults.headers, ...config.headers };
83             Object
84                 .keys(headers)
85                 .forEach(key => r.setRequestHeader(key, headers[key]));
86
87             if (config.onUploadProgress) {
88                 r.upload.addEventListener('progress', config.onUploadProgress);
89             }
90
91             // This event gets triggered on *any* server response
92             r.addEventListener('load', () => {
93                 if (r.status >= 400) {
94                     return reject(r);
95                 } else {
96                     return resolve(r);
97                 }
98             });
99
100             // This event gets triggered on network errors
101             r.addEventListener('error', () => {
102                 return reject(r);
103             });
104
105             r.upload.addEventListener('error', () => {
106                 return reject(r);
107             });
108
109             r.send(config.data);
110         });
111     }
112 }
113
114 export interface WebDAVRequestConfig {
115     headers?: {
116         [key: string]: string;
117     };
118     onUploadProgress?: (event: ProgressEvent) => void;
119 }
120
121 interface WebDAVDefaults {
122     baseURL: string;
123     headers: { [key: string]: string };
124 }
125
126 interface RequestConfig {
127     method: string;
128     url: string;
129     headers?: { [key: string]: string };
130     data?: any;
131     onUploadProgress?: (event: ProgressEvent) => void;
132 }