Comments (4)
Thanks everyone! Fixed in d5a7faf
from todoist-export.
Pasting here quickly how I fixed it:
diff --git a/src/app.js b/src/app.js
index 949b8f9..8c845c3 100644
--- a/src/app.js
+++ b/src/app.js
@@ -13,16 +13,16 @@ const config = require("./config");
const oauth2 = require("simple-oauth2").create({
client: {
id: config.client_id,
- secret: config.client_secret
+ secret: config.client_secret,
},
auth: {
tokenHost: "https://todoist.com",
tokenPath: "/oauth/access_token",
- authorizePath: "/oauth/authorize"
+ authorizePath: "/oauth/authorize",
},
options: {
- authorizationMethod: "body"
- }
+ authorizationMethod: "body",
+ },
});
const IS_PRODUCTION = process.env.NODE_ENV === "production";
@@ -41,14 +41,16 @@ const skipLogsFor = [
"/stylesheets/",
"favicon.ico",
"favicon-192.png",
- "manifest.json"
+ "manifest.json",
];
app.use(
morgan(":method :url :status", {
skip: function(request) {
- return skipLogsFor.some(part => request.originalUrl.indexOf(part) !== -1);
- }
+ return skipLogsFor.some(
+ (part) => request.originalUrl.indexOf(part) !== -1
+ );
+ },
})
);
app.use(bodyParser.json());
@@ -61,12 +63,31 @@ app.get(`${subdirectory}/`, (req, res) => {
});
const callApi = async (api, parameters) => {
- const response = await axios({
- method: "post",
- url: `https://todoist.com/API/v8/${api}`,
- data: parameters
- });
- return response.data;
+ try {
+ const response = await axios({
+ method: "post",
+ url: `https://todoist.com/API/v9/${api}`,
+ data: parameters,
+ });
+ console.log(
+ "called:",
+ response.config,
+ response.status,
+ JSON.stringify(response.headers).substring(0, 140),
+ "\n",
+ JSON.stringify(response.data).substring(0, 140)
+ );
+ return response.data;
+ } catch (err) {
+ console.error(
+ "axios failed",
+ err.config,
+ err.response.data,
+ err.response.status,
+ err.response.headers
+ );
+ throw err;
+ }
};
const renderErrorPage = (res, message, error) => {
@@ -74,20 +95,20 @@ const renderErrorPage = (res, message, error) => {
res.status((error && error.status) || 500);
res.render("error", {
message,
- error: IS_PRODUCTION ? undefined : error
+ error: IS_PRODUCTION ? undefined : error,
});
};
app.post(`${subdirectory}/auth`, (req, res) => {
var format = req.body.format || "json"; // csv vs. json
if (req.body.archived) {
- format += FORMAT_SUFFIX_INCLUDE_ARCHIVED;
+ format += FORMAT_SUFFIX_INCLUDE_ARCHIVED;
}
res.redirect(
oauth2.authorizationCode.authorizeURL({
scope: "data:read",
- state: format
+ state: format,
})
);
});
@@ -99,7 +120,7 @@ app.get(`${subdirectory}/export`, async (req, res) => {
try {
const authResponse = await oauth2.authorizationCode.getToken({
- code: req.query.code
+ code: req.query.code,
});
const token = authResponse["access_token"];
@@ -111,65 +132,81 @@ app.get(`${subdirectory}/export`, async (req, res) => {
}
});
-const escapeCommas = syncData =>
- syncData.items.map(item => ({
+const escapeCommas = (syncData) =>
+ syncData.items.map((item) => ({
...item,
labels: `"${item.labels.toString()}"`,
- content: `"${item.content.toString()}"`
+ content: `"${item.content.toString()}"`,
}));
/* Convert label IDs into their corresponding names */
-const convertLabelNames = syncData => {
+const convertLabelNames = (syncData) => {
const labelNames = syncData.labels.reduce(
(acc, label) => ({ ...acc, [label.id]: label.name }),
{}
);
- return syncData.items.map(item => ({
+ return syncData.items.map((item) => ({
...item,
- labels: item.labels.map(labelId => labelNames[labelId])
+ labels: item.labels.map((labelId) => labelNames[labelId]),
}));
};
/* Convert project IDs into their corresponding names */
-const convertProjectNames = syncData => {
+const convertProjectNames = (syncData) => {
const projectNames = syncData.projects.reduce(
(acc, project) => ({ ...acc, [project.id]: project.name }),
{}
);
- return syncData.items.map(item => ({
+ return syncData.items.map((item) => ({
...item,
- project_id: projectNames[item.project_id]
+ project_id: projectNames[item.project_id],
}));
};
/* Convert user IDs into their corresponding names */
-const convertUserNames = syncData => {
+const convertUserNames = (syncData) => {
const userNames = syncData.collaborators.reduce(
(acc, collaborator) => ({
...acc,
- [collaborator.id]: collaborator.full_name
+ [collaborator.id]: collaborator.full_name,
}),
{}
);
- return syncData.items.map(item => ({
+ return syncData.items.map((item) => ({
...item,
assigned_by_uid: userNames[item.assigned_by_uid] || null,
added_by_uid: userNames[item.added_by_uid] || null,
- user_id: userNames[item.user_id] || null
+ user_id: userNames[item.user_id] || null,
}));
};
const fetchCompleted = async function(token, offset = 0) {
- const page = await callApi("completed/get_all", { token: token, limit: COMPL_MAX_PAGE_SIZE, offset: offset })
- if (page.items.length == COMPL_MAX_PAGE_SIZE || Object.keys(page.projects).length == COMPL_MAX_PAGE_SIZE) {
- const remainder = await fetchCompleted(token, offset + COMPL_MAX_PAGE_SIZE);
- return {
- items: page.items.concat(remainder.items),
- projects: Object.assign({}, page.projects, remainder.projects),
- };
+ console.log("fetching completed items from offset", offset);
+ const page = await callApi("completed/get_all", {
+ token: token,
+ limit: COMPL_MAX_PAGE_SIZE,
+ offset: offset,
+ });
+ if (
+ page.items.length == COMPL_MAX_PAGE_SIZE ||
+ Object.keys(page.projects).length == COMPL_MAX_PAGE_SIZE
+ ) {
+ try {
+ const remainder = await fetchCompleted(
+ token,
+ offset + COMPL_MAX_PAGE_SIZE
+ );
+ return {
+ items: page.items.concat(remainder.items),
+ projects: Object.assign({}, page.projects, remainder.projects),
+ };
+ } catch (err) {
+ console.log("recursive call failed, maybe we're done?");
+ return page;
+ }
} else {
return page;
}
@@ -179,7 +216,7 @@ const exportData = async (res, token, format = "csv") => {
const syncData = await callApi("sync", {
token: token,
sync_token: "*",
- resource_types: '["all"]'
+ resource_types: '["all"]',
});
if (syncData === undefined) {
@@ -188,11 +225,14 @@ const exportData = async (res, token, format = "csv") => {
// Fetch completed tasks (premium-only)
if (format.includes(FORMAT_SUFFIX_INCLUDE_ARCHIVED)) {
- if (!syncData.user.is_premium) {
- return renderErrorPage(res, "Must be Todoist Premium to export archived items.");
- }
- format = format.replace(FORMAT_SUFFIX_INCLUDE_ARCHIVED, '');
- syncData.completed = await fetchCompleted(token);
+ if (!syncData.user.is_premium) {
+ return renderErrorPage(
+ res,
+ "Must be Todoist Premium to export archived items."
+ );
+ }
+ format = format.replace(FORMAT_SUFFIX_INCLUDE_ARCHIVED, "");
+ syncData.completed = await fetchCompleted(token);
}
if (format === "json") {
The most important changes are in callApi()
(v8 -> v9) and fetchCompleted()
(for some reason it would fail on the last call).
Excuse the sloppiness and formatting changes, I did this quickly in VS Code, just wanted to get it to work ASAP.
from todoist-export.
I'm getting the same error with 410:
Request failed with status code 410
Error: Request failed with status code 410
at createError (/home/kparal/apps/todoist-export/node_modules/axios/lib/core/createError.js:16:15)
at settle (/home/kparal/apps/todoist-export/node_modules/axios/lib/core/settle.js:17:12)
at IncomingMessage.handleStreamEnd (/home/kparal/apps/todoist-export/node_modules/axios/lib/adapters/http.js:236:11)
at IncomingMessage.emit (node:events:525:35)
at endReadableNT (node:internal/streams/readable:1358:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
from todoist-export.
Great, thanks a lot. Can you please submit a PR? Ping @darekkay
from todoist-export.
Related Issues (20)
- Add loading indicator HOT 1
- Improve state passing HOT 3
- Question: Consuming the exported JSON data HOT 3
- Not an issue, just some praise! HOT 1
- Sort CSV export to match the order in Todoist
- Export task attachments
- Conversion for AmbleNote - Link to Gist
- Export fails with "Export all" option HOT 7
- CSV export doesn't include task labels HOT 1
- api discontinued HOT 1
- 403 Invalid CSRF token when running locally HOT 3
- Suddenly running into 403 HOT 2
- fetchCompleted function fails due to inconsistent API response HOT 1
- Fetch completed items with all the data HOT 1
- Plans for archived / completed tasks? HOT 1
- export broken due to SSL HOT 6
- Package deprecation warnings - clarify minimum supported version of nodejs HOT 1
- nodejs v8 LTS vulnerabilities in dependencies with some breaking changes preventing automatic fix HOT 1
- Glitch fork HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from todoist-export.