mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-05-23 06:07:07 +00:00
fix(dataexport) - 12482 - Extract xml marshalling into library
- Add integration test on dataexport endpoint - Add library with unit test for xml marshalling
This commit is contained in:
parent
8b9c76a2b7
commit
6e91326648
5 changed files with 505 additions and 115 deletions
44
test/api/unit/libs/xmlMarshaller.test.js
Normal file
44
test/api/unit/libs/xmlMarshaller.test.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import * as xmlMarshaller from '../../../../website/server/libs/xmlMarshaller';
|
||||
|
||||
describe('xml marshaller marshalls user data', () => {
|
||||
const minimumUser = {
|
||||
pinnedItems: [],
|
||||
unpinnedItems: [],
|
||||
inbox: {},
|
||||
};
|
||||
|
||||
function userDataWith (fields) {
|
||||
return { ...minimumUser, ...fields };
|
||||
}
|
||||
|
||||
it('maps the newMessages field to have id as a value in a list.', () => {
|
||||
const userData = userDataWith({
|
||||
newMessages: {
|
||||
'283171a5-422c-4991-bc78-95b1b5b51629': {
|
||||
name: 'The Language Hackers',
|
||||
value: true,
|
||||
},
|
||||
'283171a6-422c-4991-bc78-95b1b5b51629': {
|
||||
name: 'The Bug Hackers',
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const xml = xmlMarshaller.marshallUserData(userData);
|
||||
|
||||
expect(xml).to.equal(`<user>
|
||||
<inbox/>
|
||||
<newMessages>
|
||||
<id>283171a5-422c-4991-bc78-95b1b5b51629</id>
|
||||
<name>The Language Hackers</name>
|
||||
<value>true</value>
|
||||
</newMessages>
|
||||
<newMessages>
|
||||
<id>283171a6-422c-4991-bc78-95b1b5b51629</id>
|
||||
<name>The Bug Hackers</name>
|
||||
<value>false</value>
|
||||
</newMessages>
|
||||
</user>`);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
import dataexport from '../../../../website/server/controllers/top-level/dataexport';
|
||||
|
||||
import * as Tasks from '../../../../website/server/models/task';
|
||||
import * as inboxLib from '../../../../website/server/libs/inbox';
|
||||
|
||||
describe('xml export', async () => {
|
||||
let exported;
|
||||
|
||||
const user = {
|
||||
toJSON () {
|
||||
return {
|
||||
newMessages: {
|
||||
'283171a5-422c-4991-bc78-95b1b5b51629': {
|
||||
name: 'The Language Hackers',
|
||||
value: true,
|
||||
},
|
||||
'283171a6-422c-4991-bc78-95b1b5b51629': {
|
||||
name: 'The Bug Hackers',
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
inbox: {},
|
||||
pinnedItems: [],
|
||||
unpinnedItems: [],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const response = {
|
||||
locals: { user },
|
||||
set () {},
|
||||
status: () => ({
|
||||
send: data => {
|
||||
exported = data;
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
const tasks = [{
|
||||
toJSON: () => ({ a: 'b', type: 'c' }),
|
||||
}];
|
||||
const messages = [{ flags: { content: 'message' } }];
|
||||
|
||||
sinon.stub(Tasks.Task, 'find').returns({ exec: async () => tasks });
|
||||
sinon.stub(inboxLib, 'getUserInbox').resolves(messages);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('maps the newMessages field to have id as a value in a list.', async () => {
|
||||
await dataexport.exportUserDataXml.handler({}, response);
|
||||
expect(exported).to.equal(`<user>
|
||||
<newMessages>
|
||||
<id>283171a5-422c-4991-bc78-95b1b5b51629</id>
|
||||
<name>The Language Hackers</name>
|
||||
<value>true</value>
|
||||
</newMessages>
|
||||
<newMessages>
|
||||
<id>283171a6-422c-4991-bc78-95b1b5b51629</id>
|
||||
<name>The Bug Hackers</name>
|
||||
<value>false</value>
|
||||
</newMessages>
|
||||
<inbox>
|
||||
<messages>
|
||||
<flags>content</flags>
|
||||
</messages>
|
||||
</inbox>
|
||||
<tasks>
|
||||
<cs>
|
||||
<a>b</a>
|
||||
<type>c</type>
|
||||
</cs>
|
||||
</tasks>
|
||||
</user>`);
|
||||
});
|
||||
});
|
||||
424
test/api/v3/integration/GET-dataexport.test.js
Normal file
424
test/api/v3/integration/GET-dataexport.test.js
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
import moment from 'moment';
|
||||
|
||||
import { generateUser, requester } from '../../../helpers/api-integration/v3';
|
||||
import { Task } from '../../../../website/server/models/task';
|
||||
|
||||
describe('GET /export/userdata.xml', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('returns the xml for a minimum viable user', async () => {
|
||||
const xml = await requester(user).get('/export/userdata.xml');
|
||||
|
||||
const userId = user.id;
|
||||
const userName = user.auth.local.username;
|
||||
const dateTime = moment(user.auth.timestamps.created).toDate();
|
||||
const taskId = (await Task.findOne({ userId }, 'id').exec())._id;
|
||||
|
||||
expect(xml).to.equal(`<user>
|
||||
<auth>
|
||||
<local>
|
||||
<username>${userName}</username>
|
||||
<lowerCaseUsername>${userName}</lowerCaseUsername>
|
||||
<email>${userName}@example.com</email>
|
||||
</local>
|
||||
<timestamps>
|
||||
<created>${dateTime}</created>
|
||||
<loggedin>${dateTime}</loggedin>
|
||||
<updated>${dateTime}</updated>
|
||||
</timestamps>
|
||||
<facebook/>
|
||||
<google/>
|
||||
<apple/>
|
||||
</auth>
|
||||
<achievements>
|
||||
<ultimateGearSets>
|
||||
<healer>false</healer>
|
||||
<wizard>false</wizard>
|
||||
<rogue>false</rogue>
|
||||
<warrior>false</warrior>
|
||||
</ultimateGearSets>
|
||||
<streak>0</streak>
|
||||
<perfect>0</perfect>
|
||||
<quests/>
|
||||
</achievements>
|
||||
<backer/>
|
||||
<contributor/>
|
||||
<purchased>
|
||||
<ads>false</ads>
|
||||
<txnCount>0</txnCount>
|
||||
<skin/>
|
||||
<hair/>
|
||||
<shirt/>
|
||||
<background>
|
||||
<violet>true</violet>
|
||||
</background>
|
||||
<plan>
|
||||
<consecutive>
|
||||
<count>0</count>
|
||||
<offset>0</offset>
|
||||
<gemCapExtra>0</gemCapExtra>
|
||||
<trinkets>0</trinkets>
|
||||
</consecutive>
|
||||
<quantity>1</quantity>
|
||||
<extraMonths>0</extraMonths>
|
||||
<gemsBought>0</gemsBought>
|
||||
</plan>
|
||||
</purchased>
|
||||
<flags>
|
||||
<tour>
|
||||
<intro>-2</intro>
|
||||
<classes>-2</classes>
|
||||
<stats>-2</stats>
|
||||
<tavern>-2</tavern>
|
||||
<party>-2</party>
|
||||
<guilds>-2</guilds>
|
||||
<challenges>-2</challenges>
|
||||
<market>-2</market>
|
||||
<pets>-2</pets>
|
||||
<mounts>-2</mounts>
|
||||
<hall>-2</hall>
|
||||
<equipment>-2</equipment>
|
||||
</tour>
|
||||
<tutorial>
|
||||
<common>
|
||||
<habits>false</habits>
|
||||
<dailies>false</dailies>
|
||||
<todos>false</todos>
|
||||
<rewards>false</rewards>
|
||||
<party>false</party>
|
||||
<pets>false</pets>
|
||||
<gems>false</gems>
|
||||
<skills>false</skills>
|
||||
<classes>false</classes>
|
||||
<tavern>false</tavern>
|
||||
<equipment>false</equipment>
|
||||
<items>false</items>
|
||||
<mounts>false</mounts>
|
||||
<inbox>false</inbox>
|
||||
<stats>false</stats>
|
||||
</common>
|
||||
<ios>
|
||||
<addTask>false</addTask>
|
||||
<editTask>false</editTask>
|
||||
<deleteTask>false</deleteTask>
|
||||
<filterTask>false</filterTask>
|
||||
<groupPets>false</groupPets>
|
||||
<inviteParty>false</inviteParty>
|
||||
<reorderTask>false</reorderTask>
|
||||
</ios>
|
||||
</tutorial>
|
||||
<customizationsNotification>false</customizationsNotification>
|
||||
<showTour>false</showTour>
|
||||
<dropsEnabled>false</dropsEnabled>
|
||||
<itemsEnabled>false</itemsEnabled>
|
||||
<newStuff>false</newStuff>
|
||||
<rewrite>true</rewrite>
|
||||
<classSelected>false</classSelected>
|
||||
<rebirthEnabled>false</rebirthEnabled>
|
||||
<recaptureEmailsPhase>0</recaptureEmailsPhase>
|
||||
<weeklyRecapEmailsPhase>0</weeklyRecapEmailsPhase>
|
||||
<communityGuidelinesAccepted>false</communityGuidelinesAccepted>
|
||||
<cronCount>0</cronCount>
|
||||
<welcomed>false</welcomed>
|
||||
<armoireEnabled>true</armoireEnabled>
|
||||
<armoireOpened>false</armoireOpened>
|
||||
<armoireEmpty>false</armoireEmpty>
|
||||
<cardReceived>false</cardReceived>
|
||||
<warnedLowHealth>false</warnedLowHealth>
|
||||
<verifiedUsername>true</verifiedUsername>
|
||||
<levelDrops/>
|
||||
<lastWeeklyRecap>${dateTime}</lastWeeklyRecap>
|
||||
</flags>
|
||||
<history/>
|
||||
<items>
|
||||
<gear>
|
||||
<equipped>
|
||||
<armor>armor_base_0</armor>
|
||||
<head>head_base_0</head>
|
||||
<shield>shield_base_0</shield>
|
||||
</equipped>
|
||||
<costume>
|
||||
<armor>armor_base_0</armor>
|
||||
<head>head_base_0</head>
|
||||
<shield>shield_base_0</shield>
|
||||
</costume>
|
||||
<owned>
|
||||
<headAccessory_special_blackHeadband>true</headAccessory_special_blackHeadband>
|
||||
<headAccessory_special_blueHeadband>true</headAccessory_special_blueHeadband>
|
||||
<headAccessory_special_greenHeadband>true</headAccessory_special_greenHeadband>
|
||||
<headAccessory_special_pinkHeadband>true</headAccessory_special_pinkHeadband>
|
||||
<headAccessory_special_redHeadband>true</headAccessory_special_redHeadband>
|
||||
<headAccessory_special_whiteHeadband>true</headAccessory_special_whiteHeadband>
|
||||
<headAccessory_special_yellowHeadband>true</headAccessory_special_yellowHeadband>
|
||||
<eyewear_special_blackTopFrame>true</eyewear_special_blackTopFrame>
|
||||
<eyewear_special_blueTopFrame>true</eyewear_special_blueTopFrame>
|
||||
<eyewear_special_greenTopFrame>true</eyewear_special_greenTopFrame>
|
||||
<eyewear_special_pinkTopFrame>true</eyewear_special_pinkTopFrame>
|
||||
<eyewear_special_redTopFrame>true</eyewear_special_redTopFrame>
|
||||
<eyewear_special_whiteTopFrame>true</eyewear_special_whiteTopFrame>
|
||||
<eyewear_special_yellowTopFrame>true</eyewear_special_yellowTopFrame>
|
||||
<eyewear_special_blackHalfMoon>true</eyewear_special_blackHalfMoon>
|
||||
<eyewear_special_blueHalfMoon>true</eyewear_special_blueHalfMoon>
|
||||
<eyewear_special_greenHalfMoon>true</eyewear_special_greenHalfMoon>
|
||||
<eyewear_special_pinkHalfMoon>true</eyewear_special_pinkHalfMoon>
|
||||
<eyewear_special_redHalfMoon>true</eyewear_special_redHalfMoon>
|
||||
<eyewear_special_whiteHalfMoon>true</eyewear_special_whiteHalfMoon>
|
||||
<eyewear_special_yellowHalfMoon>true</eyewear_special_yellowHalfMoon>
|
||||
</owned>
|
||||
</gear>
|
||||
<special>
|
||||
<snowball>0</snowball>
|
||||
<spookySparkles>0</spookySparkles>
|
||||
<shinySeed>0</shinySeed>
|
||||
<seafoam>0</seafoam>
|
||||
<valentine>0</valentine>
|
||||
<nye>0</nye>
|
||||
<greeting>0</greeting>
|
||||
<thankyou>0</thankyou>
|
||||
<birthday>0</birthday>
|
||||
<congrats>0</congrats>
|
||||
<getwell>0</getwell>
|
||||
<goodluck>0</goodluck>
|
||||
</special>
|
||||
<lastDrop>
|
||||
<count>0</count>
|
||||
<date>${dateTime}</date>
|
||||
</lastDrop>
|
||||
<pets/>
|
||||
<eggs/>
|
||||
<hatchingPotions/>
|
||||
<food/>
|
||||
<mounts/>
|
||||
<quests>
|
||||
<dustbunnies>1</dustbunnies>
|
||||
</quests>
|
||||
</items>
|
||||
<invitations>
|
||||
<party/>
|
||||
</invitations>
|
||||
<party>
|
||||
<quest>
|
||||
<progress>
|
||||
<up>0</up>
|
||||
<down>0</down>
|
||||
<collectedItems>0</collectedItems>
|
||||
<collect/>
|
||||
</progress>
|
||||
<RSVPNeeded>false</RSVPNeeded>
|
||||
</quest>
|
||||
<order>level</order>
|
||||
<orderAscending>ascending</orderAscending>
|
||||
</party>
|
||||
<preferences>
|
||||
<hair>
|
||||
<color>red</color>
|
||||
<base>3</base>
|
||||
<bangs>1</bangs>
|
||||
<beard>0</beard>
|
||||
<mustache>0</mustache>
|
||||
<flower>1</flower>
|
||||
</hair>
|
||||
<emailNotifications>
|
||||
<unsubscribeFromAll>false</unsubscribeFromAll>
|
||||
<newPM>true</newPM>
|
||||
<kickedGroup>true</kickedGroup>
|
||||
<wonChallenge>true</wonChallenge>
|
||||
<giftedGems>true</giftedGems>
|
||||
<giftedSubscription>true</giftedSubscription>
|
||||
<invitedParty>true</invitedParty>
|
||||
<invitedGuild>true</invitedGuild>
|
||||
<questStarted>true</questStarted>
|
||||
<invitedQuest>true</invitedQuest>
|
||||
<importantAnnouncements>true</importantAnnouncements>
|
||||
<weeklyRecaps>true</weeklyRecaps>
|
||||
<onboarding>true</onboarding>
|
||||
<majorUpdates>true</majorUpdates>
|
||||
<subscriptionReminders>true</subscriptionReminders>
|
||||
</emailNotifications>
|
||||
<pushNotifications>
|
||||
<unsubscribeFromAll>false</unsubscribeFromAll>
|
||||
<newPM>true</newPM>
|
||||
<wonChallenge>true</wonChallenge>
|
||||
<giftedGems>true</giftedGems>
|
||||
<giftedSubscription>true</giftedSubscription>
|
||||
<invitedParty>true</invitedParty>
|
||||
<invitedGuild>true</invitedGuild>
|
||||
<questStarted>true</questStarted>
|
||||
<invitedQuest>true</invitedQuest>
|
||||
<majorUpdates>true</majorUpdates>
|
||||
<mentionParty>true</mentionParty>
|
||||
<mentionJoinedGuild>true</mentionJoinedGuild>
|
||||
<mentionUnjoinedGuild>true</mentionUnjoinedGuild>
|
||||
<partyActivity>true</partyActivity>
|
||||
</pushNotifications>
|
||||
<suppressModals>
|
||||
<levelUp>false</levelUp>
|
||||
<hatchPet>false</hatchPet>
|
||||
<raisePet>false</raisePet>
|
||||
<streak>false</streak>
|
||||
</suppressModals>
|
||||
<tasks>
|
||||
<groupByChallenge>false</groupByChallenge>
|
||||
<confirmScoreNotes>false</confirmScoreNotes>
|
||||
</tasks>
|
||||
<dayStart>0</dayStart>
|
||||
<size>slim</size>
|
||||
<hideHeader>false</hideHeader>
|
||||
<skin>915533</skin>
|
||||
<shirt>blue</shirt>
|
||||
<timezoneOffset>0</timezoneOffset>
|
||||
<sound>rosstavoTheme</sound>
|
||||
<chair>none</chair>
|
||||
<allocationMode>flat</allocationMode>
|
||||
<autoEquip>true</autoEquip>
|
||||
<dateFormat>MM/dd/yyyy</dateFormat>
|
||||
<sleep>false</sleep>
|
||||
<stickyHeader>true</stickyHeader>
|
||||
<disableClasses>false</disableClasses>
|
||||
<newTaskEdit>false</newTaskEdit>
|
||||
<dailyDueDefaultView>false</dailyDueDefaultView>
|
||||
<advancedCollapsed>false</advancedCollapsed>
|
||||
<toolbarCollapsed>false</toolbarCollapsed>
|
||||
<reverseChatOrder>false</reverseChatOrder>
|
||||
<displayInviteToPartyWhenPartyIs1>true</displayInviteToPartyWhenPartyIs1>
|
||||
<language>en</language>
|
||||
<webhooks/>
|
||||
<background>violet</background>
|
||||
</preferences>
|
||||
<profile>
|
||||
<name>${userName}</name>
|
||||
</profile>
|
||||
<stats>
|
||||
<buffs>
|
||||
<str>0</str>
|
||||
<int>0</int>
|
||||
<per>0</per>
|
||||
<con>0</con>
|
||||
<stealth>0</stealth>
|
||||
<streaks>false</streaks>
|
||||
<snowball>false</snowball>
|
||||
<spookySparkles>false</spookySparkles>
|
||||
<shinySeed>false</shinySeed>
|
||||
<seafoam>false</seafoam>
|
||||
</buffs>
|
||||
<training>
|
||||
<int>0</int>
|
||||
<per>0</per>
|
||||
<str>0</str>
|
||||
<con>0</con>
|
||||
</training>
|
||||
<hp>50</hp>
|
||||
<mp>10</mp>
|
||||
<exp>0</exp>
|
||||
<gp>0</gp>
|
||||
<lvl>1</lvl>
|
||||
<class>warrior</class>
|
||||
<points>0</points>
|
||||
<str>0</str>
|
||||
<con>0</con>
|
||||
<int>0</int>
|
||||
<per>0</per>
|
||||
</stats>
|
||||
<inbox>
|
||||
<newMessages>0</newMessages>
|
||||
<optOut>false</optOut>
|
||||
</inbox>
|
||||
<tasksOrder>
|
||||
<todos>${user.tasksOrder.todos[0]}</todos>
|
||||
</tasksOrder>
|
||||
<_v>1</_v>
|
||||
<balance>0</balance>
|
||||
<loginIncentives>0</loginIncentives>
|
||||
<invitesSent>0</invitesSent>
|
||||
<_id>${userId}</_id>
|
||||
<apiToken>${user.apiToken}</apiToken>
|
||||
<lastCron>${dateTime}</lastCron>
|
||||
<tags>
|
||||
<id>${user.tags[0].id}</id>
|
||||
<name>Work</name>
|
||||
</tags>
|
||||
<tags>
|
||||
<id>${user.tags[1].id}</id>
|
||||
<name>Exercise</name>
|
||||
</tags>
|
||||
<tags>
|
||||
<id>${user.tags[2].id}</id>
|
||||
<name>Health + Wellness</name>
|
||||
</tags>
|
||||
<tags>
|
||||
<id>${user.tags[3].id}</id>
|
||||
<name>School</name>
|
||||
</tags>
|
||||
<tags>
|
||||
<id>${user.tags[4].id}</id>
|
||||
<name>Teams</name>
|
||||
</tags>
|
||||
<tags>
|
||||
<id>${user.tags[5].id}</id>
|
||||
<name>Chores</name>
|
||||
</tags>
|
||||
<tags>
|
||||
<id>${user.tags[6].id}</id>
|
||||
<name>Creativity</name>
|
||||
</tags>
|
||||
<extra/>
|
||||
<pinnedItems>
|
||||
<path>gear.flat.weapon_warrior_0</path>
|
||||
<type>marketGear</type>
|
||||
</pinnedItems>
|
||||
<pinnedItems>
|
||||
<path>gear.flat.armor_warrior_1</path>
|
||||
<type>marketGear</type>
|
||||
</pinnedItems>
|
||||
<pinnedItems>
|
||||
<path>gear.flat.shield_warrior_1</path>
|
||||
<type>marketGear</type>
|
||||
</pinnedItems>
|
||||
<pinnedItems>
|
||||
<path>gear.flat.head_warrior_1</path>
|
||||
<type>marketGear</type>
|
||||
</pinnedItems>
|
||||
<pinnedItems>
|
||||
<path>potion</path>
|
||||
<type>potion</type>
|
||||
</pinnedItems>
|
||||
<pinnedItems>
|
||||
<path>armoire</path>
|
||||
<type>armoire</type>
|
||||
</pinnedItems>
|
||||
<id>${userId}</id>
|
||||
<_tmp>undefined</_tmp>
|
||||
<tasks>
|
||||
<todos>
|
||||
<challenge/>
|
||||
<group>
|
||||
<approval>
|
||||
<required>false</required>
|
||||
<approved>false</approved>
|
||||
<requested>false</requested>
|
||||
</approval>
|
||||
<sharedCompletion>singleCompletion</sharedCompletion>
|
||||
</group>
|
||||
<completed>false</completed>
|
||||
<collapseChecklist>false</collapseChecklist>
|
||||
<type>todo</type>
|
||||
<notes>You can either complete this To Do, edit it, or remove it.</notes>
|
||||
<value>0</value>
|
||||
<priority>1</priority>
|
||||
<attribute>int</attribute>
|
||||
<byHabitica>false</byHabitica>
|
||||
<createdAt>${dateTime}</createdAt>
|
||||
<updatedAt>${dateTime}</updatedAt>
|
||||
<_id>${taskId}</_id>
|
||||
<text>Join Habitica (Check me off!)</text>
|
||||
<userId>${userId}</userId>
|
||||
<id>${taskId}</id>
|
||||
</todos>
|
||||
</tasks>
|
||||
</user>`);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import * as js2xml from 'js2xmlparser';
|
||||
// import Pageres from 'pageres';
|
||||
// import nconf from 'nconf';
|
||||
// import got from 'got';
|
||||
import md from 'habitica-markdown';
|
||||
import csvStringify from '../../libs/csvStringify';
|
||||
import {
|
||||
NotFound,
|
||||
} from '../../libs/errors';
|
||||
import { marshallUserData } from '../../libs/xmlMarshaller';
|
||||
import { NotFound } from '../../libs/errors';
|
||||
import * as Tasks from '../../models/task';
|
||||
import * as inboxLib from '../../libs/inbox';
|
||||
// import { model as User } from '../../models/user';
|
||||
|
|
@ -85,7 +83,7 @@ api.exportUserHistory = {
|
|||
|
||||
// Convert user to json and attach tasks divided by type and inbox messages
|
||||
// at user.tasks[`${taskType}s`] (user.tasks.{dailys/habits/...})
|
||||
async function _getUserDataForExport (user, xmlMode = false) {
|
||||
async function _getUserDataForExport (user) {
|
||||
const userData = user.toJSON();
|
||||
userData.tasks = {};
|
||||
|
||||
|
|
@ -108,30 +106,6 @@ async function _getUserDataForExport (user, xmlMode = false) {
|
|||
userData.tasks[`${taskType}s`] = tasksPerType;
|
||||
});
|
||||
|
||||
if (xmlMode) {
|
||||
// object maps cant be parsed
|
||||
userData.inbox.messages = _(userData.inbox.messages)
|
||||
.map(m => {
|
||||
m.flags = Object.keys(m.flags);
|
||||
|
||||
return m;
|
||||
})
|
||||
.value();
|
||||
|
||||
userData.newMessages = _.map(userData.newMessages, (msg, id) => ({ id, ...msg }));
|
||||
|
||||
// _id gets parsed as a bytearray => which gets cast to a chararray => "weird chars"
|
||||
userData.unpinnedItems = userData.unpinnedItems.map(i => ({
|
||||
path: i.path,
|
||||
type: i.type,
|
||||
}));
|
||||
|
||||
userData.pinnedItems = userData.pinnedItems.map(i => ({
|
||||
path: i.path,
|
||||
type: i.type,
|
||||
}));
|
||||
}
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
|
|
@ -172,13 +146,8 @@ api.exportUserDataXml = {
|
|||
url: '/export/userdata.xml',
|
||||
middlewares: [authWithSession],
|
||||
async handler (req, res) {
|
||||
const userData = await _getUserDataForExport(res.locals.user, true);
|
||||
const xmlData = js2xml.parse('user', userData, {
|
||||
cdataInvalidChars: true,
|
||||
declaration: {
|
||||
include: false,
|
||||
},
|
||||
});
|
||||
const userData = await _getUserDataForExport(res.locals.user);
|
||||
const xmlData = marshallUserData(userData);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'text/xml',
|
||||
|
|
|
|||
32
website/server/libs/xmlMarshaller.js
Normal file
32
website/server/libs/xmlMarshaller.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import _ from 'lodash';
|
||||
import * as js2xml from 'js2xmlparser';
|
||||
|
||||
export function marshallUserData (userData) {
|
||||
// object maps can't be marshalled to XML
|
||||
userData.inbox.messages = _(userData.inbox.messages)
|
||||
.map(m => {
|
||||
m.flags = Object.keys(m.flags);
|
||||
return m;
|
||||
})
|
||||
.value();
|
||||
|
||||
userData.newMessages = _.map(userData.newMessages, (msg, id) => ({ id, ...msg }));
|
||||
|
||||
// _id gets parsed as a bytearray => which gets cast to a chararray => "weird chars"
|
||||
userData.unpinnedItems = userData.unpinnedItems.map(i => ({
|
||||
path: i.path,
|
||||
type: i.type,
|
||||
}));
|
||||
|
||||
userData.pinnedItems = userData.pinnedItems.map(i => ({
|
||||
path: i.path,
|
||||
type: i.type,
|
||||
}));
|
||||
|
||||
return js2xml.parse('user', userData, {
|
||||
cdataInvalidChars: true,
|
||||
declaration: {
|
||||
include: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue