outlook-cli 1.2.1 → 1.2.3
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/CLI.md +16 -0
- package/README.md +110 -5
- package/cli.js +483 -70
- package/docs/REFERENCE.md +40 -2
- package/email/attachments.js +359 -0
- package/email/index.js +58 -1
- package/email/search.js +18 -12
- package/package.json +1 -1
- package/utils/graph-api.js +132 -44
package/utils/graph-api.js
CHANGED
|
@@ -5,6 +5,45 @@ const https = require('https');
|
|
|
5
5
|
const config = require('../config');
|
|
6
6
|
const mockData = require('./mock-data');
|
|
7
7
|
|
|
8
|
+
function debugLog(...args) {
|
|
9
|
+
if (config.DEBUG_LOGS) {
|
|
10
|
+
console.error(...args);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function buildEncodedPath(path) {
|
|
15
|
+
return String(path || '')
|
|
16
|
+
.split('/')
|
|
17
|
+
.map((segment) => encodeURIComponent(segment))
|
|
18
|
+
.join('/');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildQueryString(queryParams = {}) {
|
|
22
|
+
if (!queryParams || Object.keys(queryParams).length === 0) {
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const params = { ...queryParams };
|
|
27
|
+
const filter = params.$filter;
|
|
28
|
+
delete params.$filter;
|
|
29
|
+
|
|
30
|
+
let queryString = new URLSearchParams(params).toString();
|
|
31
|
+
|
|
32
|
+
if (filter) {
|
|
33
|
+
if (queryString) {
|
|
34
|
+
queryString += `&$filter=${encodeURIComponent(filter)}`;
|
|
35
|
+
} else {
|
|
36
|
+
queryString = `$filter=${encodeURIComponent(filter)}`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!queryString) {
|
|
41
|
+
return '';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return `?${queryString}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
8
47
|
/**
|
|
9
48
|
* Makes a request to the Microsoft Graph API
|
|
10
49
|
* @param {string} accessToken - The access token for authentication
|
|
@@ -17,53 +56,20 @@ const mockData = require('./mock-data');
|
|
|
17
56
|
async function callGraphAPI(accessToken, method, path, data = null, queryParams = {}) {
|
|
18
57
|
// For test tokens, we'll simulate the API call
|
|
19
58
|
if (config.USE_TEST_MODE && accessToken.startsWith('test_access_token_')) {
|
|
20
|
-
|
|
59
|
+
debugLog(`TEST MODE: Simulating ${method} ${path} API call`);
|
|
21
60
|
return mockData.simulateGraphAPIResponse(method, path, data, queryParams);
|
|
22
61
|
}
|
|
23
62
|
|
|
24
63
|
try {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Build query string from parameters with special handling for OData filters
|
|
33
|
-
let queryString = '';
|
|
34
|
-
if (Object.keys(queryParams).length > 0) {
|
|
35
|
-
// Handle $filter parameter specially to ensure proper URI encoding
|
|
36
|
-
const filter = queryParams.$filter;
|
|
37
|
-
if (filter) {
|
|
38
|
-
delete queryParams.$filter; // Remove from regular params
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Build query string with proper encoding for regular params
|
|
42
|
-
const params = new URLSearchParams();
|
|
43
|
-
for (const [key, value] of Object.entries(queryParams)) {
|
|
44
|
-
params.append(key, value);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
queryString = params.toString();
|
|
48
|
-
|
|
49
|
-
// Add filter parameter separately with proper encoding
|
|
50
|
-
if (filter) {
|
|
51
|
-
if (queryString) {
|
|
52
|
-
queryString += `&$filter=${encodeURIComponent(filter)}`;
|
|
53
|
-
} else {
|
|
54
|
-
queryString = `$filter=${encodeURIComponent(filter)}`;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (queryString) {
|
|
59
|
-
queryString = '?' + queryString;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
console.error(`Query string: ${queryString}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
64
|
+
debugLog(`Making real API call: ${method} ${path}`);
|
|
65
|
+
|
|
66
|
+
const encodedPath = buildEncodedPath(path);
|
|
67
|
+
const queryString = buildQueryString(queryParams);
|
|
68
|
+
|
|
69
|
+
debugLog(`Query string: ${queryString}`);
|
|
70
|
+
|
|
65
71
|
const url = `${config.GRAPH_API_ENDPOINT}${encodedPath}${queryString}`;
|
|
66
|
-
|
|
72
|
+
debugLog(`Full URL: ${url}`);
|
|
67
73
|
|
|
68
74
|
return new Promise((resolve, reject) => {
|
|
69
75
|
const options = {
|
|
@@ -110,11 +116,93 @@ async function callGraphAPI(accessToken, method, path, data = null, queryParams
|
|
|
110
116
|
req.end();
|
|
111
117
|
});
|
|
112
118
|
} catch (error) {
|
|
113
|
-
|
|
119
|
+
debugLog('Error calling Graph API:', error);
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Makes a raw request to the Microsoft Graph API and returns bytes.
|
|
126
|
+
* @param {string} accessToken - The access token for authentication
|
|
127
|
+
* @param {string} method - HTTP method (GET, POST, etc.)
|
|
128
|
+
* @param {string} path - API endpoint path
|
|
129
|
+
* @param {object} data - Data to send for POST/PUT requests
|
|
130
|
+
* @param {object} queryParams - Query parameters
|
|
131
|
+
* @returns {Promise<{statusCode:number, headers:object, body:Buffer}>}
|
|
132
|
+
*/
|
|
133
|
+
async function callGraphAPIRaw(accessToken, method, path, data = null, queryParams = {}) {
|
|
134
|
+
if (config.USE_TEST_MODE && accessToken.startsWith('test_access_token_')) {
|
|
135
|
+
debugLog(`TEST MODE: Simulating raw ${method} ${path} API call`);
|
|
136
|
+
return {
|
|
137
|
+
statusCode: 200,
|
|
138
|
+
headers: {
|
|
139
|
+
'content-type': 'application/octet-stream'
|
|
140
|
+
},
|
|
141
|
+
body: Buffer.from('')
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
debugLog(`Making raw API call: ${method} ${path}`);
|
|
147
|
+
|
|
148
|
+
const encodedPath = buildEncodedPath(path);
|
|
149
|
+
const queryString = buildQueryString(queryParams);
|
|
150
|
+
const url = `${config.GRAPH_API_ENDPOINT}${encodedPath}${queryString}`;
|
|
151
|
+
debugLog(`Full raw URL: ${url}`);
|
|
152
|
+
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
const options = {
|
|
155
|
+
method,
|
|
156
|
+
headers: {
|
|
157
|
+
Authorization: `Bearer ${accessToken}`
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const req = https.request(url, options, (res) => {
|
|
162
|
+
const chunks = [];
|
|
163
|
+
|
|
164
|
+
res.on('data', (chunk) => {
|
|
165
|
+
chunks.push(chunk);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
res.on('end', () => {
|
|
169
|
+
const body = Buffer.concat(chunks);
|
|
170
|
+
|
|
171
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
172
|
+
resolve({
|
|
173
|
+
statusCode: res.statusCode,
|
|
174
|
+
headers: res.headers,
|
|
175
|
+
body
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (res.statusCode === 401) {
|
|
181
|
+
reject(new Error('UNAUTHORIZED'));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
reject(new Error(`API call failed with status ${res.statusCode}: ${body.toString('utf8')}`));
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
req.on('error', (error) => {
|
|
190
|
+
reject(new Error(`Network error during API call: ${error.message}`));
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (data && (method === 'POST' || method === 'PATCH' || method === 'PUT')) {
|
|
194
|
+
req.write(JSON.stringify(data));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
req.end();
|
|
198
|
+
});
|
|
199
|
+
} catch (error) {
|
|
200
|
+
debugLog('Error calling Graph API (raw):', error);
|
|
114
201
|
throw error;
|
|
115
202
|
}
|
|
116
203
|
}
|
|
117
204
|
|
|
118
205
|
module.exports = {
|
|
119
|
-
callGraphAPI
|
|
206
|
+
callGraphAPI,
|
|
207
|
+
callGraphAPIRaw
|
|
120
208
|
};
|