mirror of https://github.com/nextcloud/contacts
245 lines
5.8 KiB
JavaScript
245 lines
5.8 KiB
JavaScript
import { multistatus } from './parser';
|
|
import * as template from './template';
|
|
|
|
/**
|
|
* Options:
|
|
*
|
|
* (String) depth - optional value for Depth header.
|
|
* (Array.<Object>) props - list of props to request.
|
|
*/
|
|
export function addressBookQuery(options) {
|
|
return collectionQuery(
|
|
template.addressBookQuery({ props: options.props || [] }),
|
|
{ depth: options.depth }
|
|
);
|
|
}
|
|
|
|
export function addressBookMultiget(options) {
|
|
return collectionQuery(
|
|
template.addressBookMultiget({ props: options.props || [], hrefs: options.hrefs || [] }),
|
|
{ depth: options.depth }
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Options:
|
|
*
|
|
* (String) data - put request body.
|
|
* (String) method - http method.
|
|
* (String) etag - cached calendar object etag.
|
|
*/
|
|
export function basic(options) {
|
|
function transformRequest(xhr) {
|
|
setRequestHeaders(xhr, options);
|
|
}
|
|
|
|
return new Request({
|
|
method: options.method,
|
|
requestData: options.data,
|
|
transformRequest: transformRequest
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Options:
|
|
*
|
|
* (String) depth - optional value for Depth header.
|
|
* (Array.<Object>) filters - list of filters to send with request.
|
|
* (Array.<Object>) props - list of props to request.
|
|
* (String) timezone - VTIMEZONE calendar object.
|
|
*/
|
|
export function calendarQuery(options) {
|
|
return collectionQuery(
|
|
template.calendarQuery({
|
|
props: options.props || [],
|
|
filters: options.filters || [],
|
|
timezone: options.timezone
|
|
}),
|
|
{
|
|
depth: options.depth
|
|
}
|
|
);
|
|
}
|
|
|
|
export function collectionQuery(requestData, options) {
|
|
function transformRequest(xhr) {
|
|
setRequestHeaders(xhr, options);
|
|
}
|
|
|
|
function transformResponse(xhr) {
|
|
return multistatus(xhr.responseText).response.map(res => {
|
|
return { href: res.href, props: getProps(res.propstat) };
|
|
});
|
|
}
|
|
|
|
return new Request({
|
|
method: 'REPORT',
|
|
requestData: requestData,
|
|
transformRequest: transformRequest,
|
|
transformResponse: transformResponse
|
|
});
|
|
}
|
|
|
|
export function mkcol(options) {
|
|
let requestData = template.mkcol({ props: options.props });
|
|
|
|
function transformRequest(xhr) {
|
|
setRequestHeaders(xhr, options);
|
|
}
|
|
|
|
return new Request({
|
|
method: 'MKCOL',
|
|
requestData: requestData,
|
|
transformRequest: transformRequest
|
|
});
|
|
}
|
|
|
|
export function proppatch(options) {
|
|
let requestData = template.proppatch({ props: options.props });
|
|
|
|
function transformRequest(xhr) {
|
|
setRequestHeaders(xhr, options);
|
|
}
|
|
|
|
return new Request({
|
|
method: 'PROPPATCH',
|
|
requestData: requestData,
|
|
transformRequest: transformRequest
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Options:
|
|
*
|
|
* (String) depth - optional value for Depth header.
|
|
* (Array.<Object>) props - list of props to request.
|
|
*/
|
|
export function propfind(options) {
|
|
let requestData = template.propfind({ props: options.props });
|
|
|
|
function transformRequest(xhr) {
|
|
setRequestHeaders(xhr, options);
|
|
}
|
|
|
|
function transformResponse(xhr) {
|
|
let responses = multistatus(xhr.responseText).response.map(res => {
|
|
return { href: res.href, props: getProps(res.propstat) };
|
|
});
|
|
|
|
if (!options.mergeResponses) {
|
|
return responses;
|
|
}
|
|
|
|
// Merge the props.
|
|
let merged = mergeProps(responses.map(res => res.props));
|
|
let hrefs = responses.map(res => res.href);
|
|
return { props: merged, hrefs: hrefs };
|
|
}
|
|
|
|
return new Request({
|
|
method: 'PROPFIND',
|
|
requestData: requestData,
|
|
transformRequest: transformRequest,
|
|
transformResponse: transformResponse
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Options:
|
|
*
|
|
* (String) depth - option value for Depth header.
|
|
* (Array.<Object>) props - list of props to request.
|
|
* (Number) syncLevel - indicates scope of the sync report request.
|
|
* (String) syncToken - synchronization token provided by the server.
|
|
*/
|
|
export function syncCollection(options) {
|
|
let requestData = template.syncCollection({
|
|
props: options.props,
|
|
syncLevel: options.syncLevel,
|
|
syncToken: options.syncToken
|
|
});
|
|
|
|
function transformRequest(xhr) {
|
|
setRequestHeaders(xhr, options);
|
|
}
|
|
|
|
function transformResponse(xhr) {
|
|
let object = multistatus(xhr.responseText);
|
|
let responses = object.response.map(res => {
|
|
return { href: res.href, props: getProps(res.propstat) };
|
|
});
|
|
|
|
return { responses: responses, syncToken: object.syncToken };
|
|
}
|
|
|
|
return new Request({
|
|
method: 'REPORT',
|
|
requestData: requestData,
|
|
transformRequest: transformRequest,
|
|
transformResponse: transformResponse
|
|
});
|
|
}
|
|
|
|
export class Request {
|
|
constructor(options={}) {
|
|
Object.assign(this, {
|
|
method: null,
|
|
requestData: null,
|
|
transformRequest: null,
|
|
transformResponse: null,
|
|
onerror: null
|
|
}, options);
|
|
}
|
|
}
|
|
|
|
function getProp(propstat) {
|
|
if (/404/g.test(propstat.status)) {
|
|
return null;
|
|
}
|
|
if (/5\d{2}/g.test(propstat.status) ||
|
|
/4\d{2}/g.test(propstat.status)) {
|
|
throw new Error('Bad status on propstat: ' + propstat.status);
|
|
}
|
|
|
|
return ('prop' in propstat) ? propstat.prop : null;
|
|
}
|
|
|
|
export function mergeProps(props) {
|
|
return props.reduce((a, b) => Object.assign(a, b), {});
|
|
}
|
|
|
|
/**
|
|
* Map propstats to props.
|
|
*/
|
|
export function getProps(propstats) {
|
|
return mergeProps(
|
|
propstats
|
|
.map(getProp)
|
|
.filter(prop => prop && typeof prop === 'object')
|
|
);
|
|
}
|
|
|
|
export function setRequestHeaders(request, options) {
|
|
if ('contentType' in options) {
|
|
request.setRequestHeader('Content-Type', options.contentType);
|
|
} else {
|
|
request.setRequestHeader('Content-Type', 'application/xml;charset=utf-8');
|
|
}
|
|
|
|
if ('depth' in options) {
|
|
request.setRequestHeader('Depth', options.depth);
|
|
}
|
|
|
|
if ('etag' in options) {
|
|
request.setRequestHeader('If-Match', options.etag);
|
|
}
|
|
|
|
if ('destination' in options) {
|
|
request.setRequestHeader('Destination', options.destination);
|
|
}
|
|
|
|
if ('overwrite' in options) {
|
|
request.setRequestHeader('Overwrite', options.overwrite);
|
|
}
|
|
}
|