@doist/todoist-api-typescript 5.8.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/authentication.js +158 -0
- package/dist/cjs/consts/endpoints.js +74 -0
- package/dist/{index.js → cjs/index.js} +1 -1
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/rest-client.js +124 -0
- package/dist/{testUtils → cjs/test-utils}/asserts.js +1 -1
- package/dist/{testUtils → cjs/test-utils}/mocks.js +8 -4
- package/dist/cjs/test-utils/msw-setup.js +27 -0
- package/dist/{testUtils/testDefaults.js → cjs/test-utils/test-defaults.js} +41 -52
- package/dist/cjs/todoist-api.js +1235 -0
- package/dist/{types → cjs/types}/entities.js +78 -31
- package/dist/cjs/types/errors.js +22 -0
- package/dist/cjs/types/http.js +22 -0
- package/dist/cjs/utils/case-conversion.js +69 -0
- package/dist/{utils → cjs/utils}/colors.js +3 -3
- package/dist/cjs/utils/fetch-with-retry.js +150 -0
- package/dist/{utils → cjs/utils}/index.js +4 -4
- package/dist/cjs/utils/multipart-upload.js +126 -0
- package/dist/{utils → cjs/utils}/processing-helpers.js +3 -3
- package/dist/{utils → cjs/utils}/sanitization.js +17 -28
- package/dist/{utils/urlHelpers.js → cjs/utils/url-helpers.js} +15 -15
- package/dist/{utils → cjs/utils}/validators.js +28 -1
- package/dist/esm/authentication.js +151 -0
- package/dist/esm/consts/endpoints.js +65 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/rest-client.js +119 -0
- package/dist/esm/test-utils/asserts.js +8 -0
- package/dist/esm/test-utils/mocks.js +10 -0
- package/dist/esm/test-utils/msw-setup.js +22 -0
- package/dist/esm/test-utils/test-defaults.js +198 -0
- package/dist/esm/todoist-api.js +1231 -0
- package/dist/esm/types/entities.js +366 -0
- package/dist/esm/types/errors.js +18 -0
- package/dist/esm/types/http.js +18 -0
- package/dist/esm/types/index.js +3 -0
- package/dist/esm/types/requests.js +1 -0
- package/dist/esm/types/sync.js +1 -0
- package/dist/esm/utils/activity-helpers.js +36 -0
- package/dist/esm/utils/case-conversion.js +61 -0
- package/dist/esm/utils/colors.js +215 -0
- package/dist/esm/utils/fetch-with-retry.js +147 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/multipart-upload.js +120 -0
- package/dist/esm/utils/processing-helpers.js +12 -0
- package/dist/esm/utils/sanitization.js +112 -0
- package/dist/esm/utils/url-helpers.js +68 -0
- package/dist/esm/utils/validators.js +97 -0
- package/dist/{authentication.d.ts → types/authentication.d.ts} +6 -1
- package/dist/{consts → types/consts}/endpoints.d.ts +11 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/rest-client.d.ts +15 -0
- package/dist/types/test-utils/msw-setup.d.ts +3 -0
- package/dist/{TodoistApi.d.ts → types/todoist-api.d.ts} +91 -2
- package/dist/types/{entities.d.ts → types/entities.d.ts} +119 -0
- package/dist/types/types/http.d.ts +68 -0
- package/dist/types/types/index.d.ts +3 -0
- package/dist/types/{requests.d.ts → types/requests.d.ts} +137 -0
- package/dist/types/utils/case-conversion.d.ts +12 -0
- package/dist/types/utils/fetch-with-retry.d.ts +11 -0
- package/dist/types/utils/index.d.ts +3 -0
- package/dist/types/utils/multipart-upload.d.ts +50 -0
- package/dist/{utils → types/utils}/validators.d.ts +7 -1
- package/package.json +24 -8
- package/dist/TodoistApi.js +0 -1209
- package/dist/authentication.js +0 -199
- package/dist/consts/endpoints.js +0 -50
- package/dist/index.d.ts +0 -4
- package/dist/restClient.d.ts +0 -5
- package/dist/restClient.js +0 -170
- package/dist/types/errors.js +0 -39
- package/dist/types/http.d.ts +0 -1
- package/dist/types/http.js +0 -2
- package/dist/utils/index.d.ts +0 -3
- /package/dist/{types → cjs/types}/index.js +0 -0
- /package/dist/{types → cjs/types}/requests.js +0 -0
- /package/dist/{types → cjs/types}/sync.js +0 -0
- /package/dist/{utils → cjs/utils}/activity-helpers.js +0 -0
- /package/dist/{testUtils → types/test-utils}/asserts.d.ts +0 -0
- /package/dist/{testUtils → types/test-utils}/mocks.d.ts +0 -0
- /package/dist/{testUtils/testDefaults.d.ts → types/test-utils/test-defaults.d.ts} +0 -0
- /package/dist/types/{errors.d.ts → types/errors.d.ts} +0 -0
- /package/dist/types/{sync.d.ts → types/sync.d.ts} +0 -0
- /package/dist/{utils → types/utils}/activity-helpers.d.ts +0 -0
- /package/dist/{utils → types/utils}/colors.d.ts +0 -0
- /package/dist/{utils → types/utils}/processing-helpers.d.ts +0 -0
- /package/dist/{utils → types/utils}/sanitization.d.ts +0 -0
- /package/dist/{utils/urlHelpers.d.ts → types/utils/url-helpers.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAuthStateParameter = getAuthStateParameter;
|
|
4
|
+
exports.getAuthorizationUrl = getAuthorizationUrl;
|
|
5
|
+
exports.getAuthToken = getAuthToken;
|
|
6
|
+
exports.revokeAuthToken = revokeAuthToken;
|
|
7
|
+
exports.revokeToken = revokeToken;
|
|
8
|
+
const rest_client_1 = require("./rest-client");
|
|
9
|
+
const uuid_1 = require("uuid");
|
|
10
|
+
const types_1 = require("./types");
|
|
11
|
+
const endpoints_1 = require("./consts/endpoints");
|
|
12
|
+
/**
|
|
13
|
+
* Creates a Basic Authentication header value from client credentials.
|
|
14
|
+
* @param clientId - The OAuth client ID
|
|
15
|
+
* @param clientSecret - The OAuth client secret
|
|
16
|
+
* @returns The Basic Auth header value (without the 'Basic ' prefix)
|
|
17
|
+
*/
|
|
18
|
+
function createBasicAuthHeader(clientId, clientSecret) {
|
|
19
|
+
const credentials = `${clientId}:${clientSecret}`;
|
|
20
|
+
return Buffer.from(credentials).toString('base64');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generates a random state parameter for OAuth2 authorization.
|
|
24
|
+
* The state parameter helps prevent CSRF attacks.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const state = getAuthStateParameter()
|
|
29
|
+
* // Store state in session
|
|
30
|
+
* const authUrl = getAuthorizationUrl(clientId, ['data:read'], state)
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @returns A random UUID v4 string
|
|
34
|
+
*/
|
|
35
|
+
function getAuthStateParameter() {
|
|
36
|
+
return (0, uuid_1.v4)();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Generates the authorization URL for the OAuth2 flow.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const url = getAuthorizationUrl(
|
|
44
|
+
* 'your-client-id',
|
|
45
|
+
* ['data:read', 'task:add'],
|
|
46
|
+
* state
|
|
47
|
+
* )
|
|
48
|
+
* // Redirect user to url
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @returns The full authorization URL to redirect users to
|
|
52
|
+
* @see https://todoist.com/api/v1/docs#tag/Authorization/OAuth
|
|
53
|
+
*/
|
|
54
|
+
function getAuthorizationUrl({ clientId, permissions, state, baseUrl, }) {
|
|
55
|
+
if (!(permissions === null || permissions === void 0 ? void 0 : permissions.length)) {
|
|
56
|
+
throw new Error('At least one scope value should be passed for permissions.');
|
|
57
|
+
}
|
|
58
|
+
const scope = permissions.join(',');
|
|
59
|
+
return `${(0, endpoints_1.getAuthBaseUri)(baseUrl)}${endpoints_1.ENDPOINT_AUTHORIZATION}?client_id=${clientId}&scope=${scope}&state=${state}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Exchanges an authorization code for an access token.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const { accessToken } = await getAuthToken({
|
|
67
|
+
* clientId: 'your-client-id',
|
|
68
|
+
* clientSecret: 'your-client-secret',
|
|
69
|
+
* code: authCode
|
|
70
|
+
* })
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @returns The access token response
|
|
74
|
+
* @throws {@link TodoistRequestError} If the token exchange fails
|
|
75
|
+
*/
|
|
76
|
+
async function getAuthToken(args, baseUrl) {
|
|
77
|
+
var _a;
|
|
78
|
+
const response = await (0, rest_client_1.request)({
|
|
79
|
+
httpMethod: 'POST',
|
|
80
|
+
baseUri: (0, endpoints_1.getAuthBaseUri)(baseUrl),
|
|
81
|
+
relativePath: endpoints_1.ENDPOINT_GET_TOKEN,
|
|
82
|
+
apiToken: undefined,
|
|
83
|
+
payload: args,
|
|
84
|
+
});
|
|
85
|
+
if (response.status !== 200 || !((_a = response.data) === null || _a === void 0 ? void 0 : _a.accessToken)) {
|
|
86
|
+
throw new types_1.TodoistRequestError('Authentication token exchange failed.', response.status, response.data);
|
|
87
|
+
}
|
|
88
|
+
return response.data;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Revokes an access token, making it invalid for future use.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* await revokeAuthToken({
|
|
96
|
+
* clientId: 'your-client-id',
|
|
97
|
+
* clientSecret: 'your-client-secret',
|
|
98
|
+
* accessToken: token
|
|
99
|
+
* })
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* @deprecated Use {@link revokeToken} instead. This function uses a legacy endpoint that will be removed in a future version. The new function uses the RFC 7009 compliant endpoint.
|
|
103
|
+
* @returns True if revocation was successful
|
|
104
|
+
* @see https://todoist.com/api/v1/docs#tag/Authorization/operation/revoke_access_token_api_api_v1_access_tokens_delete
|
|
105
|
+
*/
|
|
106
|
+
async function revokeAuthToken(args, baseUrl) {
|
|
107
|
+
const response = await (0, rest_client_1.request)({
|
|
108
|
+
httpMethod: 'POST',
|
|
109
|
+
baseUri: (0, endpoints_1.getSyncBaseUri)(baseUrl),
|
|
110
|
+
relativePath: endpoints_1.ENDPOINT_REVOKE_TOKEN,
|
|
111
|
+
apiToken: undefined,
|
|
112
|
+
payload: args,
|
|
113
|
+
});
|
|
114
|
+
return (0, rest_client_1.isSuccess)(response);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Revokes a token using the RFC 7009 OAuth 2.0 Token Revocation standard.
|
|
118
|
+
*
|
|
119
|
+
* This function uses HTTP Basic Authentication with client credentials and follows
|
|
120
|
+
* the RFC 7009 specification for token revocation.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* await revokeToken({
|
|
125
|
+
* clientId: 'your-client-id',
|
|
126
|
+
* clientSecret: 'your-client-secret',
|
|
127
|
+
* token: 'access-token-to-revoke'
|
|
128
|
+
* })
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @returns True if revocation was successful
|
|
132
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7009
|
|
133
|
+
* @see https://todoist.com/api/v1/docs#tag/Authorization
|
|
134
|
+
*/
|
|
135
|
+
async function revokeToken(args, baseUrl) {
|
|
136
|
+
const { clientId, clientSecret, token } = args;
|
|
137
|
+
// Create Basic Auth header as per RFC 7009
|
|
138
|
+
const basicAuth = createBasicAuthHeader(clientId, clientSecret);
|
|
139
|
+
const customHeaders = {
|
|
140
|
+
Authorization: `Basic ${basicAuth}`,
|
|
141
|
+
};
|
|
142
|
+
// Request body only contains the token and optional token_type_hint
|
|
143
|
+
const requestBody = {
|
|
144
|
+
token,
|
|
145
|
+
token_type_hint: 'access_token',
|
|
146
|
+
};
|
|
147
|
+
const response = await (0, rest_client_1.request)({
|
|
148
|
+
httpMethod: 'POST',
|
|
149
|
+
baseUri: (0, endpoints_1.getSyncBaseUri)(baseUrl),
|
|
150
|
+
relativePath: endpoints_1.ENDPOINT_REVOKE,
|
|
151
|
+
apiToken: undefined,
|
|
152
|
+
payload: requestBody,
|
|
153
|
+
requestId: undefined,
|
|
154
|
+
hasSyncCommands: false,
|
|
155
|
+
customHeaders: customHeaders,
|
|
156
|
+
});
|
|
157
|
+
return (0, rest_client_1.isSuccess)(response);
|
|
158
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ENDPOINT_WORKSPACE_USERS = exports.ENDPOINT_WORKSPACE_PLAN_DETAILS = exports.ENDPOINT_WORKSPACE_LOGO = exports.ENDPOINT_WORKSPACE_JOIN = exports.ENDPOINT_WORKSPACE_INVITATIONS_DELETE = exports.ENDPOINT_WORKSPACE_INVITATIONS_ALL = exports.ENDPOINT_WORKSPACE_INVITATIONS = exports.ENDPOINT_REVOKE = exports.ENDPOINT_REVOKE_TOKEN = exports.ENDPOINT_GET_TOKEN = exports.ENDPOINT_AUTHORIZATION = exports.ENDPOINT_SYNC = exports.ENDPOINT_SYNC_QUICK_ADD = exports.PROJECT_UNARCHIVE = exports.PROJECT_ARCHIVE = exports.ENDPOINT_REST_UPLOADS = exports.ENDPOINT_REST_ACTIVITIES = exports.ENDPOINT_REST_PRODUCTIVITY = exports.ENDPOINT_REST_USER = exports.ENDPOINT_REST_PROJECT_COLLABORATORS = exports.ENDPOINT_REST_PROJECTS_ARCHIVED = exports.ENDPOINT_REST_PROJECTS = exports.ENDPOINT_REST_TASK_MOVE = exports.ENDPOINT_REST_TASK_REOPEN = exports.ENDPOINT_REST_TASK_CLOSE = exports.ENDPOINT_REST_COMMENTS = exports.ENDPOINT_REST_LABELS_SHARED_REMOVE = exports.ENDPOINT_REST_LABELS_SHARED_RENAME = exports.ENDPOINT_REST_LABELS_SHARED = exports.ENDPOINT_REST_LABELS = exports.ENDPOINT_REST_SECTIONS = exports.ENDPOINT_REST_TASKS_COMPLETED_SEARCH = exports.ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE = exports.ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE = exports.ENDPOINT_REST_TASKS_FILTER = exports.ENDPOINT_REST_TASKS = exports.API_BASE_URI = exports.API_VERSION = exports.TODOIST_WEB_URI = void 0;
|
|
4
|
+
exports.getSyncBaseUri = getSyncBaseUri;
|
|
5
|
+
exports.getAuthBaseUri = getAuthBaseUri;
|
|
6
|
+
exports.getWorkspaceInvitationAcceptEndpoint = getWorkspaceInvitationAcceptEndpoint;
|
|
7
|
+
exports.getWorkspaceInvitationRejectEndpoint = getWorkspaceInvitationRejectEndpoint;
|
|
8
|
+
exports.getWorkspaceActiveProjectsEndpoint = getWorkspaceActiveProjectsEndpoint;
|
|
9
|
+
exports.getWorkspaceArchivedProjectsEndpoint = getWorkspaceArchivedProjectsEndpoint;
|
|
10
|
+
const BASE_URI = 'https://api.todoist.com';
|
|
11
|
+
const TODOIST_URI = 'https://todoist.com';
|
|
12
|
+
exports.TODOIST_WEB_URI = 'https://app.todoist.com/app';
|
|
13
|
+
// The API version is not configurable, to ensure
|
|
14
|
+
// compatibility between the API and the client.
|
|
15
|
+
exports.API_VERSION = 'v1';
|
|
16
|
+
exports.API_BASE_URI = `/api/${exports.API_VERSION}/`;
|
|
17
|
+
const API_AUTHORIZATION_BASE_URI = '/oauth/';
|
|
18
|
+
function getSyncBaseUri(domainBase = BASE_URI) {
|
|
19
|
+
return new URL(exports.API_BASE_URI, domainBase).toString();
|
|
20
|
+
}
|
|
21
|
+
function getAuthBaseUri(domainBase = TODOIST_URI) {
|
|
22
|
+
return new URL(API_AUTHORIZATION_BASE_URI, domainBase).toString();
|
|
23
|
+
}
|
|
24
|
+
exports.ENDPOINT_REST_TASKS = 'tasks';
|
|
25
|
+
exports.ENDPOINT_REST_TASKS_FILTER = exports.ENDPOINT_REST_TASKS + '/filter';
|
|
26
|
+
exports.ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE = exports.ENDPOINT_REST_TASKS + '/completed/by_completion_date';
|
|
27
|
+
exports.ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE = exports.ENDPOINT_REST_TASKS + '/completed/by_due_date';
|
|
28
|
+
exports.ENDPOINT_REST_TASKS_COMPLETED_SEARCH = 'completed/search';
|
|
29
|
+
exports.ENDPOINT_REST_SECTIONS = 'sections';
|
|
30
|
+
exports.ENDPOINT_REST_LABELS = 'labels';
|
|
31
|
+
exports.ENDPOINT_REST_LABELS_SHARED = exports.ENDPOINT_REST_LABELS + '/shared';
|
|
32
|
+
exports.ENDPOINT_REST_LABELS_SHARED_RENAME = exports.ENDPOINT_REST_LABELS_SHARED + '/rename';
|
|
33
|
+
exports.ENDPOINT_REST_LABELS_SHARED_REMOVE = exports.ENDPOINT_REST_LABELS_SHARED + '/remove';
|
|
34
|
+
exports.ENDPOINT_REST_COMMENTS = 'comments';
|
|
35
|
+
exports.ENDPOINT_REST_TASK_CLOSE = 'close';
|
|
36
|
+
exports.ENDPOINT_REST_TASK_REOPEN = 'reopen';
|
|
37
|
+
exports.ENDPOINT_REST_TASK_MOVE = 'move';
|
|
38
|
+
exports.ENDPOINT_REST_PROJECTS = 'projects';
|
|
39
|
+
exports.ENDPOINT_REST_PROJECTS_ARCHIVED = exports.ENDPOINT_REST_PROJECTS + '/archived';
|
|
40
|
+
exports.ENDPOINT_REST_PROJECT_COLLABORATORS = 'collaborators';
|
|
41
|
+
exports.ENDPOINT_REST_USER = 'user';
|
|
42
|
+
exports.ENDPOINT_REST_PRODUCTIVITY = exports.ENDPOINT_REST_TASKS + '/completed/stats';
|
|
43
|
+
exports.ENDPOINT_REST_ACTIVITIES = 'activities';
|
|
44
|
+
exports.ENDPOINT_REST_UPLOADS = 'uploads';
|
|
45
|
+
exports.PROJECT_ARCHIVE = 'archive';
|
|
46
|
+
exports.PROJECT_UNARCHIVE = 'unarchive';
|
|
47
|
+
exports.ENDPOINT_SYNC_QUICK_ADD = exports.ENDPOINT_REST_TASKS + '/quick';
|
|
48
|
+
exports.ENDPOINT_SYNC = 'sync';
|
|
49
|
+
exports.ENDPOINT_AUTHORIZATION = 'authorize';
|
|
50
|
+
exports.ENDPOINT_GET_TOKEN = 'access_token';
|
|
51
|
+
exports.ENDPOINT_REVOKE_TOKEN = 'access_tokens/revoke';
|
|
52
|
+
exports.ENDPOINT_REVOKE = 'revoke';
|
|
53
|
+
// Workspace endpoints
|
|
54
|
+
exports.ENDPOINT_WORKSPACE_INVITATIONS = 'workspaces/invitations';
|
|
55
|
+
exports.ENDPOINT_WORKSPACE_INVITATIONS_ALL = 'workspaces/invitations/all';
|
|
56
|
+
exports.ENDPOINT_WORKSPACE_INVITATIONS_DELETE = 'workspaces/invitations/delete';
|
|
57
|
+
exports.ENDPOINT_WORKSPACE_JOIN = 'workspaces/join';
|
|
58
|
+
exports.ENDPOINT_WORKSPACE_LOGO = 'workspaces/logo';
|
|
59
|
+
exports.ENDPOINT_WORKSPACE_PLAN_DETAILS = 'workspaces/plan_details';
|
|
60
|
+
exports.ENDPOINT_WORKSPACE_USERS = 'workspaces/users';
|
|
61
|
+
// Workspace invitation actions (require invite_code parameter)
|
|
62
|
+
function getWorkspaceInvitationAcceptEndpoint(inviteCode) {
|
|
63
|
+
return `workspaces/invitations/${inviteCode}/accept`;
|
|
64
|
+
}
|
|
65
|
+
function getWorkspaceInvitationRejectEndpoint(inviteCode) {
|
|
66
|
+
return `workspaces/invitations/${inviteCode}/reject`;
|
|
67
|
+
}
|
|
68
|
+
// Workspace projects (require workspace_id parameter)
|
|
69
|
+
function getWorkspaceActiveProjectsEndpoint(workspaceId) {
|
|
70
|
+
return `workspaces/${workspaceId}/projects/active`;
|
|
71
|
+
}
|
|
72
|
+
function getWorkspaceArchivedProjectsEndpoint(workspaceId) {
|
|
73
|
+
return `workspaces/${workspaceId}/projects/archived`;
|
|
74
|
+
}
|
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./todoist-api"), exports);
|
|
18
18
|
__exportStar(require("./authentication"), exports);
|
|
19
19
|
__exportStar(require("./types"), exports);
|
|
20
20
|
__exportStar(require("./utils"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.paramsSerializer = paramsSerializer;
|
|
4
|
+
exports.isSuccess = isSuccess;
|
|
5
|
+
exports.request = request;
|
|
6
|
+
const errors_1 = require("./types/errors");
|
|
7
|
+
const http_1 = require("./types/http");
|
|
8
|
+
const uuid_1 = require("uuid");
|
|
9
|
+
const endpoints_1 = require("./consts/endpoints");
|
|
10
|
+
const case_conversion_1 = require("./utils/case-conversion");
|
|
11
|
+
const fetch_with_retry_1 = require("./utils/fetch-with-retry");
|
|
12
|
+
function paramsSerializer(params) {
|
|
13
|
+
const qs = new URLSearchParams();
|
|
14
|
+
Object.keys(params).forEach((key) => {
|
|
15
|
+
const value = params[key];
|
|
16
|
+
if (value != null) {
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
qs.append(key, value.join(','));
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
qs.append(key, String(value));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return qs.toString();
|
|
26
|
+
}
|
|
27
|
+
const defaultHeaders = {
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
};
|
|
30
|
+
function getAuthHeader(apiKey) {
|
|
31
|
+
return `Bearer ${apiKey}`;
|
|
32
|
+
}
|
|
33
|
+
function getRetryDelay(retryCount) {
|
|
34
|
+
return retryCount === 1 ? 0 : 500;
|
|
35
|
+
}
|
|
36
|
+
function getTodoistRequestError(args) {
|
|
37
|
+
const { error, originalStack } = args;
|
|
38
|
+
const requestError = new errors_1.TodoistRequestError(error.message);
|
|
39
|
+
requestError.stack = originalStack ? originalStack.stack : error.stack;
|
|
40
|
+
if ((0, http_1.isHttpError)(error)) {
|
|
41
|
+
requestError.httpStatusCode = error.status;
|
|
42
|
+
requestError.responseData = error.data;
|
|
43
|
+
}
|
|
44
|
+
return requestError;
|
|
45
|
+
}
|
|
46
|
+
function getRequestConfiguration(args) {
|
|
47
|
+
const { baseURL, apiToken, requestId, customHeaders } = args;
|
|
48
|
+
const authHeader = apiToken ? { Authorization: getAuthHeader(apiToken) } : undefined;
|
|
49
|
+
const requestIdHeader = requestId ? { 'X-Request-Id': requestId } : undefined;
|
|
50
|
+
const headers = Object.assign(Object.assign(Object.assign(Object.assign({}, defaultHeaders), authHeader), requestIdHeader), customHeaders);
|
|
51
|
+
return { baseURL, headers };
|
|
52
|
+
}
|
|
53
|
+
function getHttpClientConfig(args) {
|
|
54
|
+
const { baseURL, apiToken, requestId, customHeaders } = args;
|
|
55
|
+
const configuration = getRequestConfiguration({ baseURL, apiToken, requestId, customHeaders });
|
|
56
|
+
return Object.assign(Object.assign({}, configuration), { timeout: 30000, retry: {
|
|
57
|
+
retries: 3,
|
|
58
|
+
retryCondition: http_1.isNetworkError,
|
|
59
|
+
retryDelay: getRetryDelay,
|
|
60
|
+
} });
|
|
61
|
+
}
|
|
62
|
+
function isSuccess(response) {
|
|
63
|
+
return response.status >= 200 && response.status < 300;
|
|
64
|
+
}
|
|
65
|
+
async function request(args) {
|
|
66
|
+
const { httpMethod, baseUri, relativePath, apiToken, payload, requestId: initialRequestId, hasSyncCommands, customHeaders, } = args;
|
|
67
|
+
// Capture original stack for better error reporting
|
|
68
|
+
const originalStack = new Error();
|
|
69
|
+
try {
|
|
70
|
+
let requestId = initialRequestId;
|
|
71
|
+
// Sync api don't allow a request id in the CORS
|
|
72
|
+
if (httpMethod === 'POST' && !requestId && !baseUri.includes(endpoints_1.API_BASE_URI)) {
|
|
73
|
+
requestId = (0, uuid_1.v4)();
|
|
74
|
+
}
|
|
75
|
+
const config = getHttpClientConfig({ baseURL: baseUri, apiToken, requestId, customHeaders });
|
|
76
|
+
const url = `${baseUri}${relativePath}`;
|
|
77
|
+
const fetchOptions = {
|
|
78
|
+
method: httpMethod,
|
|
79
|
+
headers: config.headers,
|
|
80
|
+
timeout: config.timeout,
|
|
81
|
+
};
|
|
82
|
+
let finalUrl = url;
|
|
83
|
+
switch (httpMethod) {
|
|
84
|
+
case 'GET':
|
|
85
|
+
// For GET requests, add query parameters to URL
|
|
86
|
+
if (payload) {
|
|
87
|
+
const queryString = paramsSerializer(payload);
|
|
88
|
+
if (queryString) {
|
|
89
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
90
|
+
finalUrl = `${url}${separator}${queryString}`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case 'POST':
|
|
95
|
+
case 'PUT': {
|
|
96
|
+
// Convert payload from camelCase to snake_case
|
|
97
|
+
const convertedPayload = payload ? (0, case_conversion_1.snakeCaseKeys)(payload) : payload;
|
|
98
|
+
const body = hasSyncCommands
|
|
99
|
+
? JSON.stringify(convertedPayload)
|
|
100
|
+
: JSON.stringify(convertedPayload);
|
|
101
|
+
fetchOptions.body = body;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'DELETE':
|
|
105
|
+
// DELETE requests don't have a body
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
// Make the request
|
|
109
|
+
const response = await (0, fetch_with_retry_1.fetchWithRetry)({
|
|
110
|
+
url: finalUrl,
|
|
111
|
+
options: fetchOptions,
|
|
112
|
+
retryConfig: config.retry,
|
|
113
|
+
});
|
|
114
|
+
// Convert snake_case response to camelCase
|
|
115
|
+
const convertedData = (0, case_conversion_1.camelCaseKeys)(response.data);
|
|
116
|
+
return Object.assign(Object.assign({}, response), { data: convertedData });
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (!(error instanceof Error)) {
|
|
120
|
+
throw new Error('An unknown error occurred during the request');
|
|
121
|
+
}
|
|
122
|
+
throw getTodoistRequestError({ error, originalStack });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -34,9 +34,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.setupRestClientMock = setupRestClientMock;
|
|
37
|
-
|
|
38
|
-
function setupRestClientMock(responseData, status) {
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
const restClient = __importStar(require("../rest-client"));
|
|
38
|
+
function setupRestClientMock(responseData, status = 200) {
|
|
39
|
+
const response = {
|
|
40
|
+
status,
|
|
41
|
+
statusText: status === 200 ? 'OK' : 'Error',
|
|
42
|
+
headers: {},
|
|
43
|
+
data: responseData,
|
|
44
|
+
};
|
|
41
45
|
return jest.spyOn(restClient, 'request').mockResolvedValue(response);
|
|
42
46
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpResponse = exports.http = exports.server = exports.handlers = void 0;
|
|
4
|
+
const node_1 = require("msw/node");
|
|
5
|
+
// Default handlers for common API responses
|
|
6
|
+
exports.handlers = [
|
|
7
|
+
// Default handlers can be added here for common endpoints
|
|
8
|
+
// Individual test files will add their own specific handlers
|
|
9
|
+
];
|
|
10
|
+
// Create MSW server instance
|
|
11
|
+
exports.server = (0, node_1.setupServer)(...exports.handlers);
|
|
12
|
+
// Setup MSW for tests
|
|
13
|
+
beforeAll(() => {
|
|
14
|
+
exports.server.listen({
|
|
15
|
+
onUnhandledRequest: 'warn', // Log warnings for unhandled requests during development
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
exports.server.resetHandlers(); // Reset handlers between tests
|
|
20
|
+
});
|
|
21
|
+
afterAll(() => {
|
|
22
|
+
exports.server.close(); // Clean up after all tests
|
|
23
|
+
});
|
|
24
|
+
// Export MSW utilities for use in tests
|
|
25
|
+
var msw_1 = require("msw");
|
|
26
|
+
Object.defineProperty(exports, "http", { enumerable: true, get: function () { return msw_1.http; } });
|
|
27
|
+
Object.defineProperty(exports, "HttpResponse", { enumerable: true, get: function () { return msw_1.HttpResponse; } });
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
3
|
exports.COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = exports.COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = exports.COMMENT_WITH_OPTIONALS_AS_NULL_TASK = exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK = exports.INVALID_COMMENT = exports.DEFAULT_COMMENT = exports.DEFAULT_RAW_COMMENT = exports.INVALID_ATTACHMENT = exports.DEFAULT_ATTACHMENT = exports.INVALID_USER = exports.DEFAULT_USER = exports.INVALID_LABEL = exports.DEFAULT_LABEL = exports.INVALID_SECTION = exports.DEFAULT_SECTION = exports.PROJECT_WITH_OPTIONALS_AS_NULL = exports.INVALID_PROJECT = exports.DEFAULT_PROJECT = exports.TASK_WITH_OPTIONALS_AS_NULL = exports.INVALID_TASK = exports.DEFAULT_TASK = exports.DEFAULT_DEADLINE = exports.DEFAULT_DURATION = exports.DEFAULT_DUE_DATE = exports.INVALID_ENTITY_ID = exports.DEFAULT_REQUEST_ID = exports.DEFAULT_AUTH_TOKEN = exports.DEFAULT_PROJECT_VIEW_STYLE = exports.DEFAULT_PROJECT_NAME = exports.DEFAULT_PROJECT_ID = exports.DEFAULT_ORDER = exports.DEFAULT_TASK_PRIORITY = exports.DEFAULT_TASK_DESCRIPTION = exports.DEFAULT_TASK_CONTENT = exports.DEFAULT_TASK_ID = void 0;
|
|
15
|
-
|
|
4
|
+
const url_helpers_1 = require("../utils/url-helpers");
|
|
16
5
|
exports.DEFAULT_TASK_ID = '1234';
|
|
17
6
|
exports.DEFAULT_TASK_CONTENT = 'This is a task';
|
|
18
7
|
exports.DEFAULT_TASK_DESCRIPTION = 'A description';
|
|
@@ -21,32 +10,32 @@ exports.DEFAULT_ORDER = 3;
|
|
|
21
10
|
exports.DEFAULT_PROJECT_ID = '123';
|
|
22
11
|
exports.DEFAULT_PROJECT_NAME = 'This is a project';
|
|
23
12
|
exports.DEFAULT_PROJECT_VIEW_STYLE = 'list';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
13
|
+
const DEFAULT_LABEL_ID = '456';
|
|
14
|
+
const DEFAULT_LABEL_NAME = 'This is a label';
|
|
15
|
+
const DEFAULT_SECTION_ID = '456';
|
|
16
|
+
const DEFAULT_SECTION_NAME = 'This is a section';
|
|
17
|
+
const DEFAULT_PARENT_ID = '5678';
|
|
18
|
+
const DEFAULT_ASSIGNEE = '1234';
|
|
19
|
+
const DEFAULT_CREATOR = '1234';
|
|
20
|
+
const DEFAULT_DATE = '2020-09-08T12:00:00Z';
|
|
21
|
+
const DEFAULT_ENTITY_COLOR = 'berry_red';
|
|
22
|
+
const DEFAULT_LABELS = ['personal', 'work', 'hobby'];
|
|
23
|
+
const DEFAULT_USER_ID = '5';
|
|
24
|
+
const DEFAULT_USER_NAME = 'A User';
|
|
25
|
+
const DEFAULT_USER_EMAIL = 'atestuser@doist.com';
|
|
26
|
+
const DEFAULT_COMMENT_ID = '4';
|
|
27
|
+
const DEFAULT_COMMENT_CONTENT = 'A comment';
|
|
28
|
+
const DEFAULT_COMMENT_REACTIONS = { '👍': ['1234', '5678'] };
|
|
29
|
+
const DEFAULT_NOTE_COUNT = 0;
|
|
30
|
+
const DEFAULT_CAN_ASSIGN_TASKS = true;
|
|
31
|
+
const DEFAULT_IS_ARCHIVED = false;
|
|
32
|
+
const DEFAULT_IS_DELETED = false;
|
|
33
|
+
const DEFAULT_IS_FROZEN = false;
|
|
34
|
+
const DEFAULT_IS_COLLAPSED = false;
|
|
46
35
|
// URL constants using the helper functions
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
const DEFAULT_TASK_URL = (0, url_helpers_1.getTaskUrl)(exports.DEFAULT_TASK_ID, exports.DEFAULT_TASK_CONTENT);
|
|
37
|
+
const DEFAULT_PROJECT_URL = (0, url_helpers_1.getProjectUrl)(exports.DEFAULT_PROJECT_ID, exports.DEFAULT_PROJECT_NAME);
|
|
38
|
+
const DEFAULT_SECTION_URL = (0, url_helpers_1.getSectionUrl)(DEFAULT_SECTION_ID, DEFAULT_SECTION_NAME);
|
|
50
39
|
exports.DEFAULT_AUTH_TOKEN = 'AToken';
|
|
51
40
|
exports.DEFAULT_REQUEST_ID = 'ARequestID';
|
|
52
41
|
exports.INVALID_ENTITY_ID = 1234;
|
|
@@ -92,7 +81,7 @@ exports.DEFAULT_TASK = {
|
|
|
92
81
|
isCollapsed: DEFAULT_IS_COLLAPSED,
|
|
93
82
|
url: DEFAULT_TASK_URL,
|
|
94
83
|
};
|
|
95
|
-
exports.INVALID_TASK =
|
|
84
|
+
exports.INVALID_TASK = Object.assign(Object.assign({}, exports.DEFAULT_TASK), { due: '2020-01-31' });
|
|
96
85
|
exports.TASK_WITH_OPTIONALS_AS_NULL = {
|
|
97
86
|
userId: DEFAULT_CREATOR,
|
|
98
87
|
id: exports.DEFAULT_TASK_ID,
|
|
@@ -141,8 +130,8 @@ exports.DEFAULT_PROJECT = {
|
|
|
141
130
|
isCollapsed: DEFAULT_IS_COLLAPSED,
|
|
142
131
|
url: DEFAULT_PROJECT_URL,
|
|
143
132
|
};
|
|
144
|
-
exports.INVALID_PROJECT =
|
|
145
|
-
exports.PROJECT_WITH_OPTIONALS_AS_NULL =
|
|
133
|
+
exports.INVALID_PROJECT = Object.assign(Object.assign({}, exports.DEFAULT_PROJECT), { name: 123 });
|
|
134
|
+
exports.PROJECT_WITH_OPTIONALS_AS_NULL = Object.assign(Object.assign({}, exports.DEFAULT_PROJECT), { parentId: null });
|
|
146
135
|
exports.DEFAULT_SECTION = {
|
|
147
136
|
id: DEFAULT_SECTION_ID,
|
|
148
137
|
userId: DEFAULT_USER_ID,
|
|
@@ -157,7 +146,7 @@ exports.DEFAULT_SECTION = {
|
|
|
157
146
|
isCollapsed: false,
|
|
158
147
|
url: DEFAULT_SECTION_URL,
|
|
159
148
|
};
|
|
160
|
-
exports.INVALID_SECTION =
|
|
149
|
+
exports.INVALID_SECTION = Object.assign(Object.assign({}, exports.DEFAULT_SECTION), { projectId: undefined });
|
|
161
150
|
exports.DEFAULT_LABEL = {
|
|
162
151
|
id: DEFAULT_LABEL_ID,
|
|
163
152
|
name: DEFAULT_LABEL_NAME,
|
|
@@ -165,20 +154,20 @@ exports.DEFAULT_LABEL = {
|
|
|
165
154
|
order: exports.DEFAULT_ORDER,
|
|
166
155
|
isFavorite: false,
|
|
167
156
|
};
|
|
168
|
-
exports.INVALID_LABEL =
|
|
157
|
+
exports.INVALID_LABEL = Object.assign(Object.assign({}, exports.DEFAULT_LABEL), { isFavorite: 'true' });
|
|
169
158
|
exports.DEFAULT_USER = {
|
|
170
159
|
id: DEFAULT_USER_ID,
|
|
171
160
|
name: DEFAULT_USER_NAME,
|
|
172
161
|
email: DEFAULT_USER_EMAIL,
|
|
173
162
|
};
|
|
174
|
-
exports.INVALID_USER =
|
|
163
|
+
exports.INVALID_USER = Object.assign(Object.assign({}, exports.DEFAULT_USER), { email: undefined });
|
|
175
164
|
exports.DEFAULT_ATTACHMENT = {
|
|
176
165
|
resourceType: 'file',
|
|
177
166
|
fileType: 'image/png',
|
|
178
167
|
fileUrl: 'https://someurl.com/image.jpg',
|
|
179
168
|
uploadState: 'completed',
|
|
180
169
|
};
|
|
181
|
-
exports.INVALID_ATTACHMENT =
|
|
170
|
+
exports.INVALID_ATTACHMENT = Object.assign(Object.assign({}, exports.DEFAULT_ATTACHMENT), { uploadState: 'something random' });
|
|
182
171
|
exports.DEFAULT_RAW_COMMENT = {
|
|
183
172
|
id: DEFAULT_COMMENT_ID,
|
|
184
173
|
postedUid: DEFAULT_USER_ID,
|
|
@@ -190,11 +179,11 @@ exports.DEFAULT_RAW_COMMENT = {
|
|
|
190
179
|
reactions: DEFAULT_COMMENT_REACTIONS,
|
|
191
180
|
itemId: exports.DEFAULT_TASK_ID,
|
|
192
181
|
};
|
|
193
|
-
exports.DEFAULT_COMMENT =
|
|
194
|
-
exports.INVALID_COMMENT =
|
|
195
|
-
exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK =
|
|
196
|
-
exports.COMMENT_WITH_OPTIONALS_AS_NULL_TASK =
|
|
197
|
-
exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL =
|
|
182
|
+
exports.DEFAULT_COMMENT = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { taskId: exports.DEFAULT_RAW_COMMENT.itemId, itemId: undefined });
|
|
183
|
+
exports.INVALID_COMMENT = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { isDeleted: 'true' });
|
|
184
|
+
exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { fileAttachment: null, uidsToNotify: null, reactions: null });
|
|
185
|
+
exports.COMMENT_WITH_OPTIONALS_AS_NULL_TASK = Object.assign(Object.assign({}, exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK), { taskId: exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK.itemId, itemId: undefined });
|
|
186
|
+
exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { fileAttachment: {
|
|
198
187
|
resourceType: 'file',
|
|
199
188
|
fileName: null,
|
|
200
189
|
fileSize: null,
|
|
@@ -207,6 +196,6 @@ exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = __assign(__assign({
|
|
|
207
196
|
url: null,
|
|
208
197
|
title: null,
|
|
209
198
|
} });
|
|
210
|
-
exports.COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL =
|
|
211
|
-
exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT =
|
|
212
|
-
exports.COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT =
|
|
199
|
+
exports.COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = Object.assign(Object.assign({}, exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL), { taskId: exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL.itemId, itemId: undefined });
|
|
200
|
+
exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { itemId: undefined, projectId: exports.DEFAULT_PROJECT_ID });
|
|
201
|
+
exports.COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = Object.assign(Object.assign({}, exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT), { taskId: undefined });
|