test: migration from Python to JavaScript

This commit is contained in:
Francis Lachapelle
2021-11-08 16:49:23 -05:00
parent a5c315fd17
commit 2f739fdc21
3 changed files with 266 additions and 195 deletions
+216 -192
View File
@@ -1,3 +1,4 @@
import cookie from 'cookie'
import {
DAVNamespace,
DAVNamespaceShorthandMap,
@@ -42,7 +43,10 @@ export {
class WebDAV {
constructor(un, pw) {
this.serverUrl = `http://${config.hostname}:${config.port}`
this.cookie = null
if (un && pw) {
this.username = un
this.password = pw
this.headers = getBasicAuthHeaders({
username: un,
password: pw
@@ -53,6 +57,54 @@ class WebDAV {
}
}
// Generic operations
async getAuthCookie() {
if (!this.cookie) {
const resource = `/SOGo/connect`
const response = await fetch(this.serverUrl + resource, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({userName: this.username, password: this.password})
})
const values = response.headers.get('set-cookie').split(/, /)
let authCookies = []
for (let v of values) {
let c = cookie.parse(v)
for (let authCookie of ['0xHIGHFLYxSOGo', 'XSRF-TOKEN']) {
if (Object.keys(c).includes('0xHIGHFLYxSOGo')) {
authCookies.push(cookie.serialize(authCookie, c[authCookie]))
}
}
}
this.cookie = authCookies.join('; ')
}
return this.cookie
}
async getHttp(resource) {
const authCookie = await this.getAuthCookie()
const localHeaders = { Cookie: authCookie }
return await fetch(this.serverUrl + resource, {
method: 'GET',
headers: localHeaders
})
}
async postHttp(resource, contentType = 'application/json', data = '') {
const authCookie = await this.getAuthCookie()
const localHeaders = { 'Content-Type': contentType, Cookie: authCookie }
return await fetch(this.serverUrl + resource, {
method: 'POST',
body: data,
headers: localHeaders
})
}
// WebDAV operations
deleteObject(resource) {
return deleteObject({
url: this.serverUrl + resource,
@@ -60,37 +112,6 @@ class WebDAV {
})
}
makeCalendar(resource) {
return makeCalendar({
url: this.serverUrl + resource,
headers: this.headers
})
}
createCalendarObject(resource, filename, calendar) {
return createCalendarObject({
headers: this.headers,
calendar: { url: this.serverUrl + resource }, // DAVCalendar
filename: filename,
iCalString: calendar
})
}
postCaldav(resource, vcalendar, originator, recipients) {
let localHeaders = { 'content-type': 'text/calendar; charset=utf-8'}
if (originator)
localHeaders.originator = originator
if (recipients && recipients.length > 0)
localHeaders.recipients = recipients.join(',')
return fetch(this.serverUrl + resource, {
method: 'POST',
body: vcalendar,
headers: { ...this.headers, ...localHeaders }
})
}
getObject(resource, filename) {
let url
if (resource.match(/^http/))
@@ -110,6 +131,13 @@ class WebDAV {
})
}
makeCollection(resource) {
return makeCollection({
url: this.serverUrl + resource,
headers: this.headers
});
}
propfindWebdav(resource, properties, namespace = DAVNamespace.DAV, headers = {}) {
const nsShort = DAVnsShortMap[namespace] || DAVInverseShort
const formattedProperties = properties.map(p => {
@@ -132,7 +160,13 @@ class WebDAV {
body: {
propfind: {
_attributes: {
...getDAVAttribute([DAVNamespace.DAV]),
...getDAVAttribute([
DAVNamespace.CALDAV,
DAVNamespace.CALDAV_APPLE,
DAVNamespace.CALENDAR_SERVER,
DAVNamespace.CARDDAV,
DAVNamespace.DAV
]),
[`xmlns:${nsShort}`]: namespace
},
prop: formattedProperties
@@ -179,6 +213,151 @@ class WebDAV {
})
}
propfindURL(resource = '/SOGo/dav') {
return propfind({
url: this.serverUrl + resource,
depth: '1',
props: [
{ name: 'displayname', namespace: DAVNamespace.DAV },
{ name: 'resourcetype', namespace: DAVNamespace.DAV }
],
headers: this.headers
})
}
propfindCollection(resource) {
return propfind({
url: this.serverUrl + resource,
headers: this.headers
})
}
// http://tools.ietf.org/html/rfc3253.html#section-3.8
expendProperty(resource, properties) {
return davRequest({
url: this.serverUrl + resource,
init: {
method: 'REPORT',
namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV],
headers: this.headers,
body: {
'expand-property': {
_attributes: getDAVAttribute([
DAVNamespace.DAV,
]),
[`${DAVNamespaceShorthandMap[DAVNamespace.DAV]}:property`]: properties
}
}
},
})
}
proppatchWebdav(resource, properties, namespace = DAVNamespace.DAV, headers = {}) {
const nsShort = DAVNamespaceShorthandMap[namespace] || DAVInverseShort
const formattedProperties = Object.keys(properties).map(p => {
if (Array.isArray(properties[p])) {
return { [`${nsShort}:${p}`]: properties[p].map(pp => {
const [ key ] = Object.keys(pp)
return { [`${nsShort}:${key}`]: pp[key] || '' }
})}
}
return { [`${nsShort}:${p}`]: properties[p] || '' }
})
if (typeof headers.depth == 'undefined') {
headers.depth = new String(0)
}
return davRequest({
url: this.serverUrl + resource,
init: {
method: 'PROPPATCH',
headers: { ...this.headers, ...headers },
namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV],
body: {
propertyupdate: {
_attributes: {
...getDAVAttribute([
DAVNamespace.CALDAV,
DAVNamespace.CALDAV_APPLE,
DAVNamespace.CALENDAR_SERVER,
DAVNamespace.CARDDAV,
DAVNamespace.DAV
]),
[`xmlns:${nsShort}`]: namespace
},
set: {
prop: formattedProperties
}
}
}
}
})
}
currentUserPrivilegeSet(resource) {
return propfind({
url: this.serverUrl + resource,
depth: '0',
props: [
{ name: 'current-user-privilege-set', namespace: DAVNamespace.DAV }
],
headers: this.headers
})
}
options(resource) {
return davRequest({
url: this.serverUrl + resource,
init: {
method: 'OPTIONS',
headers: this.headers,
body: null
},
convertIncoming: false
})
}
principalCollectionSet(resource = '/SOGo/dav') {
return propfind({
url: this.serverUrl + resource,
depth: '0',
props: [{ name: 'principal-collection-set', namespace: DAVNamespace.DAV }],
headers: this.headers
})
}
// CalDAV operations
makeCalendar(resource) {
return makeCalendar({
url: this.serverUrl + resource,
headers: this.headers
})
}
createCalendarObject(resource, filename, calendar) {
return createCalendarObject({
headers: this.headers,
calendar: { url: this.serverUrl + resource }, // DAVCalendar
filename: filename,
iCalString: calendar
})
}
postCaldav(resource, vcalendar, originator, recipients) {
let localHeaders = { 'content-type': 'text/calendar; charset=utf-8'}
if (originator)
localHeaders.originator = originator
if (recipients && recipients.length > 0)
localHeaders.recipients = recipients.join(',')
return fetch(this.serverUrl + resource, {
method: 'POST',
body: vcalendar,
headers: { ...this.headers, ...localHeaders }
})
}
propfindEvent(resource) {
return propfind({
url: this.serverUrl + resource,
@@ -201,34 +380,6 @@ class WebDAV {
})
}
principalCollectionSet(resource = '/SOGo/dav') {
return propfind({
url: this.serverUrl + resource,
depth: '0',
props: [{ name: 'principal-collection-set', namespace: DAVNamespace.DAV }],
headers: this.headers
})
}
propfindURL(resource = '/SOGo/dav') {
return propfind({
url: this.serverUrl + resource,
depth: '1',
props: [
{ name: 'displayname', namespace: DAVNamespace.DAV },
{ name: 'resourcetype', namespace: DAVNamespace.DAV }
],
headers: this.headers
})
}
propfindCollection(resource) {
return propfind({
url: this.serverUrl + resource,
headers: this.headers
})
}
principalPropertySearch(resource) {
return davRequest({
url: `${this.serverUrl}/SOGo/dav`,
@@ -255,26 +406,6 @@ class WebDAV {
})
}
// http://tools.ietf.org/html/rfc3253.html#section-3.8
expendProperty(resource, properties) {
return davRequest({
url: this.serverUrl + resource,
init: {
method: 'REPORT',
namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV],
headers: this.headers,
body: {
'expand-property': {
_attributes: getDAVAttribute([
DAVNamespace.DAV,
]),
[`${DAVNamespaceShorthandMap[DAVNamespace.DAV]}:property`]: properties
}
}
},
})
}
syncColletion(resource) {
return davRequest({
url: this.serverUrl + resource,
@@ -330,112 +461,15 @@ class WebDAV {
})
}
propfindCaldav(resource, properties, depth = 0, parseOutgoing = true) {
const formattedProperties = properties.map(p => { return { name: p, namespace: DAVNamespace.CALDAV } })
return davRequest({
url: this.serverUrl + resource,
init: {
method: 'PROPFIND',
headers: { ...this.headers, depth: new String(depth) },
namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV],
body: {
propfind: {
_attributes: getDAVAttribute([
DAVNamespace.CALDAV,
DAVNamespace.CALDAV_APPLE,
DAVNamespace.CALENDAR_SERVER,
DAVNamespace.CARDDAV,
DAVNamespace.DAV
]),
prop: formattedProperties.length ? formatProps(formattedProperties) : null,
}
}
},
parseOutgoing
})
propfindCaldav(resource, properties, depth = 0) {
return this.propfindWebdav(resource, properties, DAVNamespace.CALDAV, { depth: new String(depth) })
}
proppatchCaldav(resource, properties, headers = {}) {
const formattedProperties = Object.keys(properties).map(p => {
return { name: p, namespace: DAVNamespace.CALDAV, value: properties[p] }
})
return davRequest({
url: this.serverUrl + resource,
init: {
method: 'PROPPATCH',
headers: { ...this.headers, ...headers },
namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV],
body: {
propertyupdate: {
_attributes: getDAVAttribute([
DAVNamespace.CALDAV,
DAVNamespace.CALDAV_APPLE,
DAVNamespace.CALENDAR_SERVER,
DAVNamespace.CARDDAV,
DAVNamespace.DAV
]),
set: {
prop: formatProps(formattedProperties)
}
}
}
}
// parseOutgoing
})
return this.proppatchWebdav(resource, properties, DAVNamespace.CALDAV, headers)
}
proppatchWebdav(resource, properties, namespace = DAVNamespace.DAV, headers = {}) {
const nsShort = DAVNamespaceShorthandMap[namespace] || DAVInverseShort
const formattedProperties = Object.keys(properties).map(p => {
if (Array.isArray(properties[p])) {
return { [`${nsShort}:${p}`]: properties[p].map(pp => {
const [ key ] = Object.keys(pp)
return { [`${nsShort}:${key}`]: pp[key] || '' }
})}
}
return { [`${nsShort}:${p}`]: properties[p] || '' }
})
if (typeof headers.depth == 'undefined') {
headers.depth = new String(0)
}
return davRequest({
url: this.serverUrl + resource,
init: {
method: 'PROPPATCH',
headers: { ...this.headers, ...headers },
namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV],
body: {
propertyupdate: {
_attributes: {
...getDAVAttribute([DAVNamespace.DAV]),
[`xmlns:${nsShort}`]: namespace
},
set: {
prop: formattedProperties
}
}
}
}
})
}
currentUserPrivilegeSet(resource) {
return propfind({
url: this.serverUrl + resource,
depth: '0',
props: [
{ name: 'current-user-privilege-set', namespace: DAVNamespace.DAV }
],
headers: this.headers
})
}
makeCollection(resource) {
return makeCollection({
url: this.serverUrl + resource,
headers: this.headers
});
}
// CardDAV operations
getCard(resource, filename) {
return davRequest({
@@ -458,17 +492,7 @@ class WebDAV {
})
}
options(resource) {
return davRequest({
url: this.serverUrl + resource,
init: {
method: 'OPTIONS',
headers: this.headers,
body: null
},
convertIncoming: false
})
}
// MailDAV operations
mailQueryMaildav(resource, properties, filters = {}, sort, ascending = true) {
let formattedFilters = {}
+50
View File
@@ -0,0 +1,50 @@
import config from '../lib/config'
import WebDAV from '../lib/WebDAV'
let webdav, resource
describe('HTTP Calendar', function() {
beforeAll(async function() {
webdav = new WebDAV(config.username, config.password)
resource = `/SOGo/so/${config.username}/Calendar`
})
it('Add Web Calendar', async function() {
const data = { url: config.webCalendarURL }
let url, response, body
url = `${resource}/addWebCalendar`
response = await webdav.postHttp(url, 'application/json', JSON.stringify(data))
expect(response.status)
.withContext(`HTTP status code when subscribing to a Web calendar`)
.toBe(200)
body = await response.json()
expect(Object.keys(body))
.withContext(`JSON payload when subscribing to a Web calendar`)
.toContain('id')
const calID = body.id
url = `${resource}/${calID}/reload`
response = await webdav.getHttp(url)
expect(response.status)
.withContext(`HTTP status code when reloading a Web calendar`)
.toBe(200)
expect(response.headers.get('content-type'))
.withContext(`Content type of response when reloading a Web calendar`)
.toBe('application/json')
body = await response.json()
expect(Object.keys(body))
.withContext(`JSON payload when reloading a Web calendar`)
.toContain('imported')
url = `${resource}/${calID}/delete`
response = await webdav.postHttp(url, 'application/json')
expect(response.status)
.withContext(`HTTP status code when unsubscribing to a Web calendar`)
.toBe(204)
})
})
-3
View File
@@ -8,9 +8,6 @@ describe('webdav sync', function() {
const webdav_su = new WebDAV(config.superuser, config.superuser_password)
const resource = `/SOGo/dav/${config.username}/Calendar/test-webdavsync/`
beforeEach(async function() {
})
afterEach(async function() {
await webdav_su.deleteObject(resource)
})