-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbackground.js
146 lines (127 loc) · 4.14 KB
/
background.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
async function fetchBlobUrl(url) {
return new Promise((resolve, reject) => {
// Need to use XHR to avoid CORS issues apparently. :(
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200)
resolve(this.response);
else
reject(this);
};
xhr.send();
});
};
function stringify(obj) {
return JSON.stringify(obj, (key, value) => {
// Prevent IDs from being converted to scientific notation.
if (typeof value === 'number')
return String(value);
return value;
});
}
const MAX_TASKS = 25;
class TaskQueue {
constructor() {
this.tasks = [];
this.inProgressTaskCount = 0;
this.resolves = [];
}
doTasks() {
if (this.inProgressTaskCount >= MAX_TASKS) return;
let task = this.tasks.shift();
if (task === undefined) {
for (let resolve of this.resolves) resolve();
return;
}
this.inProgressTaskCount++;
task().then(() => {
this.inProgressTaskCount--;
this.doTasks();
});
this.doTasks();
}
queueTask(task) {
const shouldStart = this.tasks.length == 0;
this.tasks.push(task);
if (shouldStart) this.doTasks();
}
flush() {
return new Promise((resolve) => {
if (!this.tasks.length && this.inProgressTaskCount == 0) resolve();
this.resolves.push(resolve);
});
}
}
const taskQueue = new TaskQueue();
async function createExport(tabId) {
async function sendFetchRequest(options) {
return new Promise(resolve => {
chrome.tabs.sendMessage(tabId, options, (response) => {
resolve(response);
});
});
}
async function fetchData(options) {
return (await sendFetchRequest(options)).data;
};
var zip = new JSZip();
let projectMetadata = await fetchData({json: "https://app.asana.com/api/1.0/projects"});
let projectData = await Promise.all(projectMetadata.map(x => fetchData(
{json: `https://app.asana.com/api/1.0/projects/${x.gid}/tasks?opt_pretty&opt_expand=(this%7Csubtasks%2B)`})));
let attachments = [];
await Promise.all(projectData.map(async tasks => {
tasks.map(task => {
taskQueue.queueTask(async () => {
let data = await fetchData({json: `https://app.asana.com/api/1.0/tasks/${task.gid}/attachments`});
for (let attachment of data) {
let attachmentMetadata = await fetchData({json: `https://app.asana.com/api/1.0/attachments/${attachment.gid}`});
// Dropbox download URLs incorrectly have both dl=0 and dl=1 and the dl=0 wins.
// Force dl=1 so it gets the dropbox download instead of the viewer.
let blobUrl = await sendFetchRequest({blob: attachmentMetadata.download_url.replace('dl=0', 'dl=1')});
zip.folder('attachments').folder(task.id).file(attachment.name, await fetchBlobUrl(blobUrl));
attachments.push({
taskId: task.id,
data: attachmentMetadata,
});
}
});
});
}));
await taskQueue.flush();
zip.file("projects.json", stringify(projectMetadata));
zip.file("tasks.json", stringify(projectData));
zip.file("attachments.json", stringify(attachments));
let date = new Date();
let blob = await zip.generateAsync({type:"blob"});
chrome.downloads.download({
url: URL.createObjectURL(blob),
filename: `asana-${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}.zip`
});
};
chrome.browserAction.onClicked.addListener(async (extensionTab) => {
chrome.tabs.create({url: 'https://app.asana.com'}, (asanaTab) => {
chrome.tabs.executeScript(asanaTab.id,
// Need to have asana.com credentials, so do fetches via content script.
{code: `
async function fetchJson(url) {
let resp = await fetch(url);
return await resp.json();
};
async function fetchBlob(url) {
let resp = await fetch(url);
return await resp.blob();
};
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.json) {
fetchJson(msg.json).then(x=> sendResponse(x));
} else {
fetchBlob(msg.blob).then(x => sendResponse(URL.createObjectURL(x)));
}
return true;
});
`},
(result) => createExport(asanaTab.id));
})
});