@west10tech/notion-mcp 1.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 +120 -0
- package/dist/clients/notion-client.d.ts +53 -0
- package/dist/clients/notion-client.d.ts.map +1 -0
- package/dist/clients/notion-client.js +2950 -0
- package/dist/clients/notion-client.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +163 -0
- package/dist/index.js.map +1 -0
- package/dist/services/log-batcher.d.ts +44 -0
- package/dist/services/log-batcher.d.ts.map +1 -0
- package/dist/services/log-batcher.js +81 -0
- package/dist/services/log-batcher.js.map +1 -0
- package/dist/services/log-shipper.d.ts +104 -0
- package/dist/services/log-shipper.d.ts.map +1 -0
- package/dist/services/log-shipper.js +384 -0
- package/dist/services/log-shipper.js.map +1 -0
- package/dist/services/logger.d.ts +92 -0
- package/dist/services/logger.d.ts.map +1 -0
- package/dist/services/logger.js +224 -0
- package/dist/services/logger.js.map +1 -0
- package/dist/services/progress-reporter.d.ts +64 -0
- package/dist/services/progress-reporter.d.ts.map +1 -0
- package/dist/services/progress-reporter.js +192 -0
- package/dist/services/progress-reporter.js.map +1 -0
- package/dist/services/request-tracker.d.ts +55 -0
- package/dist/services/request-tracker.d.ts.map +1 -0
- package/dist/services/request-tracker.js +184 -0
- package/dist/services/request-tracker.js.map +1 -0
- package/dist/tools/notion-tools.d.ts +21 -0
- package/dist/tools/notion-tools.d.ts.map +1 -0
- package/dist/tools/notion-tools.js +1146 -0
- package/dist/tools/notion-tools.js.map +1 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,2950 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { Logger } from '../services/logger.js';
|
|
3
|
+
export class NotionClient {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
// Generate unique session ID for this client instance
|
|
7
|
+
this.sessionId = `notion-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
8
|
+
// Initialize logger (fallback to console if not provided)
|
|
9
|
+
this.logger = config.logger || new Logger({
|
|
10
|
+
logLevel: 'ERROR',
|
|
11
|
+
component: 'client',
|
|
12
|
+
enableConsole: true,
|
|
13
|
+
enableShipping: false,
|
|
14
|
+
serverName: 'notion-mcp'
|
|
15
|
+
});
|
|
16
|
+
this.logger.info('CLIENT_INIT', 'Client instance created', {
|
|
17
|
+
baseUrl: this.resolveBaseUrl(),
|
|
18
|
+
timeout: this.config.timeout || 30000,
|
|
19
|
+
hasRateLimit: !!this.config.rateLimit,
|
|
20
|
+
configKeys: Object.keys(config)
|
|
21
|
+
});
|
|
22
|
+
this.httpClient = axios.create({
|
|
23
|
+
baseURL: this.resolveBaseUrl(),
|
|
24
|
+
timeout: this.config.timeout || 30000,
|
|
25
|
+
headers: {
|
|
26
|
+
'Accept': 'application/json',
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
'User-Agent': 'notion-mcp/1.0.0',
|
|
29
|
+
'Notion-Version': '2022-06-28',
|
|
30
|
+
...this.getAuthHeaders()
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
// Add request interceptor for rate limiting
|
|
34
|
+
if (this.config.rateLimit) {
|
|
35
|
+
this.setupRateLimit(this.config.rateLimit);
|
|
36
|
+
}
|
|
37
|
+
// Add request interceptor for logging
|
|
38
|
+
this.httpClient.interceptors.request.use((config) => {
|
|
39
|
+
this.logger.logRequestStart(config.method?.toUpperCase() || 'GET', `${config.baseURL}${config.url}`, {
|
|
40
|
+
hasData: !!config.data,
|
|
41
|
+
hasParams: !!(config.params && Object.keys(config.params).length > 0),
|
|
42
|
+
headers: Object.keys(config.headers || {})
|
|
43
|
+
});
|
|
44
|
+
if (config.data) {
|
|
45
|
+
this.logger.debug('HTTP_REQUEST_BODY', 'Request body data', {
|
|
46
|
+
dataType: typeof config.data,
|
|
47
|
+
dataSize: JSON.stringify(config.data).length
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (config.params && Object.keys(config.params).length > 0) {
|
|
51
|
+
this.logger.debug('HTTP_REQUEST_PARAMS', 'Query parameters', {
|
|
52
|
+
paramCount: Object.keys(config.params).length,
|
|
53
|
+
paramKeys: Object.keys(config.params)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return config;
|
|
57
|
+
}, (error) => {
|
|
58
|
+
this.logger.error('HTTP_REQUEST_ERROR', 'Request interceptor error', {
|
|
59
|
+
error: error.message,
|
|
60
|
+
code: error.code
|
|
61
|
+
});
|
|
62
|
+
return Promise.reject(error);
|
|
63
|
+
});
|
|
64
|
+
// Add response interceptor for logging and error handling
|
|
65
|
+
this.httpClient.interceptors.response.use((response) => {
|
|
66
|
+
this.logger.logRequestSuccess(response.config?.method?.toUpperCase() || 'GET', `${response.config?.baseURL}${response.config?.url}`, response.status, 0, // Duration will be calculated in endpoint methods
|
|
67
|
+
{
|
|
68
|
+
statusText: response.statusText,
|
|
69
|
+
responseSize: JSON.stringify(response.data).length,
|
|
70
|
+
headers: Object.keys(response.headers || {})
|
|
71
|
+
});
|
|
72
|
+
return response;
|
|
73
|
+
}, (error) => {
|
|
74
|
+
this.logger.logRequestError(error.config?.method?.toUpperCase() || 'GET', `${error.config?.baseURL}${error.config?.url}`, error, 0, // Duration will be calculated in endpoint methods
|
|
75
|
+
{
|
|
76
|
+
hasResponseData: !!error.response?.data
|
|
77
|
+
});
|
|
78
|
+
throw error;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
setupRateLimit(requestsPerMinute) {
|
|
82
|
+
const interval = 60000 / requestsPerMinute; // ms between requests
|
|
83
|
+
let lastRequestTime = 0;
|
|
84
|
+
this.logger.info('RATE_LIMIT_SETUP', 'Rate limiting configured', {
|
|
85
|
+
requestsPerMinute,
|
|
86
|
+
intervalMs: interval
|
|
87
|
+
});
|
|
88
|
+
this.httpClient.interceptors.request.use(async (config) => {
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
const timeSinceLastRequest = now - lastRequestTime;
|
|
91
|
+
if (timeSinceLastRequest < interval) {
|
|
92
|
+
const delayMs = interval - timeSinceLastRequest;
|
|
93
|
+
this.logger.logRateLimit('HTTP_REQUEST', delayMs, {
|
|
94
|
+
timeSinceLastRequest,
|
|
95
|
+
requiredInterval: interval
|
|
96
|
+
});
|
|
97
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
98
|
+
}
|
|
99
|
+
lastRequestTime = Date.now();
|
|
100
|
+
return config;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
resolveBaseUrl() {
|
|
104
|
+
// Debug logging for base_url resolution
|
|
105
|
+
// console.error('[NotionClient] Resolving base URL...');
|
|
106
|
+
// console.error('[NotionClient] Template base_url:', 'https://api.notion.com/v1');
|
|
107
|
+
// console.error('[NotionClient] CustomConfig baseUrl:', '');
|
|
108
|
+
let baseUrl = 'https://api.notion.com/v1';
|
|
109
|
+
// console.error('[NotionClient] Initial resolved baseUrl:', baseUrl);
|
|
110
|
+
// If no base URL was found, throw an error
|
|
111
|
+
if (!baseUrl) {
|
|
112
|
+
throw new Error(`No base URL configured for notion. Please provide base_url in template or customConfig.baseUrl.`);
|
|
113
|
+
}
|
|
114
|
+
// Handle dynamic domain replacement for patterns like CONFLUENCE_DOMAIN, JIRA_DOMAIN, etc.
|
|
115
|
+
const domainEnvVar = `NOTION_DOMAIN`;
|
|
116
|
+
const domain = process.env[domainEnvVar];
|
|
117
|
+
// console.error(`[NotionClient] Domain env var (${domainEnvVar}):`, domain);
|
|
118
|
+
// Check for SERVICE_DOMAIN pattern (e.g., CONFLUENCE_DOMAIN, JIRA_DOMAIN, SLACK_DOMAIN)
|
|
119
|
+
// This handles both YOUR_DOMAIN and {SERVICE}_DOMAIN patterns in base URLs
|
|
120
|
+
if (baseUrl.includes('YOUR_DOMAIN') || baseUrl.includes(`${domainEnvVar}`)) {
|
|
121
|
+
if (!domain) {
|
|
122
|
+
throw new Error(`Missing domain configuration. Please set ${domainEnvVar} environment variable.`);
|
|
123
|
+
}
|
|
124
|
+
// Replace the placeholder with the actual domain value
|
|
125
|
+
// This handles patterns like https://CONFLUENCE_DOMAIN.atlassian.net
|
|
126
|
+
if (baseUrl.includes('YOUR_DOMAIN')) {
|
|
127
|
+
baseUrl = baseUrl.replace(/YOUR_DOMAIN/g, domain);
|
|
128
|
+
}
|
|
129
|
+
if (baseUrl.includes(`${domainEnvVar}`)) {
|
|
130
|
+
// Replace all occurrences of the service-specific domain placeholder
|
|
131
|
+
const regex = new RegExp(domainEnvVar, 'g');
|
|
132
|
+
baseUrl = baseUrl.replace(regex, domain);
|
|
133
|
+
}
|
|
134
|
+
this.logger.info('DOMAIN_RESOLVED', `Resolved base URL with domain`, {
|
|
135
|
+
template: 'notion',
|
|
136
|
+
baseUrl: baseUrl
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// console.error('[NotionClient] Final resolved baseUrl:', baseUrl);
|
|
140
|
+
return baseUrl;
|
|
141
|
+
}
|
|
142
|
+
getAuthHeaders() {
|
|
143
|
+
// Bearer/API key authentication (static tokens)
|
|
144
|
+
// Determine the correct environment variable name based on auth type and configuration
|
|
145
|
+
let envVarName;
|
|
146
|
+
// Use first required env var if specified
|
|
147
|
+
envVarName = 'NOTION_ACCESS_TOKEN';
|
|
148
|
+
const token = this.config.authToken || this.config['nOTIONACCESSTOKEN'] || process.env[envVarName];
|
|
149
|
+
if (token) {
|
|
150
|
+
this.logger.logAuthEvent('static_token_auth_setup', true, {
|
|
151
|
+
authType: 'bearer',
|
|
152
|
+
tokenPreview: token.substring(0, 8) + '...',
|
|
153
|
+
header: 'Authorization',
|
|
154
|
+
source: 'static_configuration',
|
|
155
|
+
envVar: envVarName
|
|
156
|
+
});
|
|
157
|
+
return {
|
|
158
|
+
'Authorization': `Bearer ${token}`
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
this.logger.warn('AUTH_WARNING', 'No authentication token found', {
|
|
162
|
+
authType: 'bearer',
|
|
163
|
+
warning: 'API calls may be rate limited',
|
|
164
|
+
checkedSources: ['config.authToken', 'environment variables'],
|
|
165
|
+
expectedEnvVar: envVarName
|
|
166
|
+
});
|
|
167
|
+
return {};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Initialize the client (for OAuth clients that need initialization)
|
|
171
|
+
*/
|
|
172
|
+
async initialize() {
|
|
173
|
+
this.logger.debug('CLIENT_INITIALIZE', 'No initialization required for this auth type');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get the session ID for this client instance
|
|
177
|
+
*/
|
|
178
|
+
getSessionId() {
|
|
179
|
+
return this.sessionId;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Make an authenticated request with proper headers and cancellation support
|
|
183
|
+
*/
|
|
184
|
+
async makeAuthenticatedRequest(config, options) {
|
|
185
|
+
// Add abort signal if provided
|
|
186
|
+
if (options?.signal) {
|
|
187
|
+
config.signal = options.signal;
|
|
188
|
+
}
|
|
189
|
+
// For non-OAuth requests, log what auth headers are being used
|
|
190
|
+
this.logger.info('REQUEST_AUTH', 'Using pre-configured authentication headers', {
|
|
191
|
+
authType: 'static',
|
|
192
|
+
requestUrl: config.url,
|
|
193
|
+
authHeaders: config.headers?.Authorization ? 'present' : 'missing',
|
|
194
|
+
headerKeys: Object.keys(config.headers || {})
|
|
195
|
+
});
|
|
196
|
+
return this.httpClient.request(config);
|
|
197
|
+
}
|
|
198
|
+
buildPath(template, params) {
|
|
199
|
+
let path = template;
|
|
200
|
+
// Custom encoding that preserves forward slashes for API paths
|
|
201
|
+
const encodePathComponent = (value) => {
|
|
202
|
+
// For Google API resource names like "people/c123", preserve the forward slash
|
|
203
|
+
return encodeURIComponent(value).replace(/%2F/g, '/');
|
|
204
|
+
};
|
|
205
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
206
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
207
|
+
let match;
|
|
208
|
+
const processedParams = [];
|
|
209
|
+
while ((match = googlePathTemplateRegex.exec(template)) !== null) {
|
|
210
|
+
const fullMatch = match[0]; // e.g., "{resourceName=people/*}"
|
|
211
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
212
|
+
if (paramName && params[paramName] !== undefined) {
|
|
213
|
+
path = path.replace(fullMatch, encodePathComponent(String(params[paramName])));
|
|
214
|
+
processedParams.push(paramName);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Handle standard path templates: {resourceName}
|
|
218
|
+
for (const [key, value] of Object.entries(params)) {
|
|
219
|
+
if (!processedParams.includes(key)) {
|
|
220
|
+
const standardTemplate = `{${key}}`;
|
|
221
|
+
if (path.includes(standardTemplate)) {
|
|
222
|
+
path = path.replace(standardTemplate, encodePathComponent(String(value)));
|
|
223
|
+
processedParams.push(key);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
this.logger.debug('PATH_BUILD', 'Built API path from template', {
|
|
228
|
+
template,
|
|
229
|
+
resultPath: path,
|
|
230
|
+
paramCount: Object.keys(params).length,
|
|
231
|
+
paramKeys: Object.keys(params),
|
|
232
|
+
processedParams,
|
|
233
|
+
hasGoogleTemplates: googlePathTemplateRegex.test(template)
|
|
234
|
+
});
|
|
235
|
+
return path;
|
|
236
|
+
}
|
|
237
|
+
/* DEBUG: endpoint={"name":"list_databases","method":"GET","path":"/databases","description":"⚠️ DEPRECATED: This endpoint is deprecated by Notion API. Use the search endpoint with database filter instead.","parameters":{"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"query"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"query","default":100}},"response_format":"json","category":"Database Operations"} */
|
|
238
|
+
async listDatabases(params, options) {
|
|
239
|
+
const startTime = Date.now();
|
|
240
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
241
|
+
endpoint: 'list_databases',
|
|
242
|
+
method: 'GET',
|
|
243
|
+
path: '/databases',
|
|
244
|
+
paramCount: Object.keys(params || {}).length,
|
|
245
|
+
paramKeys: Object.keys(params || {})
|
|
246
|
+
});
|
|
247
|
+
try {
|
|
248
|
+
// Extract and separate parameters by location: path, query, body
|
|
249
|
+
const pathTemplate = '/databases';
|
|
250
|
+
const pathParams = {};
|
|
251
|
+
const queryParams = {};
|
|
252
|
+
const bodyParams = {};
|
|
253
|
+
const extractedParams = [];
|
|
254
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
255
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
256
|
+
let match;
|
|
257
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
258
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
259
|
+
if (paramName && params[paramName] !== undefined) {
|
|
260
|
+
pathParams[paramName] = params[paramName];
|
|
261
|
+
extractedParams.push(paramName);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Handle standard path templates: {resourceName}
|
|
265
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
266
|
+
standardPathParams.forEach(paramTemplate => {
|
|
267
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
268
|
+
// Only process if not already handled by Google template logic
|
|
269
|
+
if (!extractedParams.includes(paramName)) {
|
|
270
|
+
if (params[paramName] !== undefined) {
|
|
271
|
+
pathParams[paramName] = params[paramName];
|
|
272
|
+
extractedParams.push(paramName);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// Provide default values for optional path parameters
|
|
276
|
+
if (paramName === 'userId') {
|
|
277
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
278
|
+
extractedParams.push(paramName);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
// Check if any parameter has raw_array flag
|
|
284
|
+
let hasRawArrayBody = false;
|
|
285
|
+
let rawBodyData = undefined;
|
|
286
|
+
// Separate remaining parameters by location (query vs body)
|
|
287
|
+
if (params[""] !== undefined) {
|
|
288
|
+
queryParams[""] = params[""];
|
|
289
|
+
extractedParams.push("");
|
|
290
|
+
}
|
|
291
|
+
if (params[""] !== undefined) {
|
|
292
|
+
queryParams[""] = params[""];
|
|
293
|
+
extractedParams.push("");
|
|
294
|
+
}
|
|
295
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
296
|
+
for (const [key, value] of Object.entries(params)) {
|
|
297
|
+
if (!extractedParams.includes(key)) {
|
|
298
|
+
bodyParams[key] = value;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Validate required parameters
|
|
302
|
+
const path = this.buildPath('/databases', pathParams);
|
|
303
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
304
|
+
const requestPath = path === '/' ? '' : path;
|
|
305
|
+
// Report initial progress if callback provided
|
|
306
|
+
if (options?.onProgress) {
|
|
307
|
+
await options.onProgress({
|
|
308
|
+
progress: 0,
|
|
309
|
+
total: 100,
|
|
310
|
+
message: `Starting list_databases request...`
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
314
|
+
const requestConfig = {};
|
|
315
|
+
if (options?.signal) {
|
|
316
|
+
requestConfig.signal = options.signal;
|
|
317
|
+
}
|
|
318
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
319
|
+
// Report completion progress if callback provided
|
|
320
|
+
if (options?.onProgress) {
|
|
321
|
+
await options.onProgress({
|
|
322
|
+
progress: 100,
|
|
323
|
+
total: 100,
|
|
324
|
+
message: `Completed list_databases request`
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
const duration = Date.now() - startTime;
|
|
328
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
329
|
+
endpoint: 'list_databases',
|
|
330
|
+
method: 'GET',
|
|
331
|
+
path: '/databases',
|
|
332
|
+
duration_ms: duration,
|
|
333
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
334
|
+
});
|
|
335
|
+
return {
|
|
336
|
+
content: [
|
|
337
|
+
{
|
|
338
|
+
type: 'text',
|
|
339
|
+
text: JSON.stringify(response.data, null, 2)
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
const duration = Date.now() - startTime;
|
|
346
|
+
// Check if error is due to cancellation
|
|
347
|
+
if (axios.isCancel(error)) {
|
|
348
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
349
|
+
endpoint: 'list_databases',
|
|
350
|
+
method: 'GET',
|
|
351
|
+
path: '/databases',
|
|
352
|
+
duration_ms: duration
|
|
353
|
+
});
|
|
354
|
+
throw new Error('Request was cancelled');
|
|
355
|
+
}
|
|
356
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
357
|
+
endpoint: 'list_databases',
|
|
358
|
+
method: 'GET',
|
|
359
|
+
path: '/databases',
|
|
360
|
+
duration_ms: duration,
|
|
361
|
+
error: error instanceof Error ? error.message : String(error),
|
|
362
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
363
|
+
});
|
|
364
|
+
throw new Error(`Failed to execute list_databases: ${error instanceof Error ? error.message : String(error)}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/* DEBUG: endpoint={"name":"get_database","method":"GET","path":"/databases/{database_id}","description":"Get database by ID","parameters":{"database_id":{"type":"string","required":true,"description":"Database ID to fetch","location":"path"}},"response_format":"json","category":"Database Operations"} */
|
|
368
|
+
async getDatabase(params, options) {
|
|
369
|
+
const startTime = Date.now();
|
|
370
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
371
|
+
endpoint: 'get_database',
|
|
372
|
+
method: 'GET',
|
|
373
|
+
path: '/databases/{database_id}',
|
|
374
|
+
paramCount: Object.keys(params || {}).length,
|
|
375
|
+
paramKeys: Object.keys(params || {})
|
|
376
|
+
});
|
|
377
|
+
try {
|
|
378
|
+
// Extract and separate parameters by location: path, query, body
|
|
379
|
+
const pathTemplate = '/databases/{database_id}';
|
|
380
|
+
const pathParams = {};
|
|
381
|
+
const queryParams = {};
|
|
382
|
+
const bodyParams = {};
|
|
383
|
+
const extractedParams = [];
|
|
384
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
385
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
386
|
+
let match;
|
|
387
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
388
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
389
|
+
if (paramName && params[paramName] !== undefined) {
|
|
390
|
+
pathParams[paramName] = params[paramName];
|
|
391
|
+
extractedParams.push(paramName);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Handle standard path templates: {resourceName}
|
|
395
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
396
|
+
standardPathParams.forEach(paramTemplate => {
|
|
397
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
398
|
+
// Only process if not already handled by Google template logic
|
|
399
|
+
if (!extractedParams.includes(paramName)) {
|
|
400
|
+
if (params[paramName] !== undefined) {
|
|
401
|
+
pathParams[paramName] = params[paramName];
|
|
402
|
+
extractedParams.push(paramName);
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
// Provide default values for optional path parameters
|
|
406
|
+
if (paramName === 'userId') {
|
|
407
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
408
|
+
extractedParams.push(paramName);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
// Check if any parameter has raw_array flag
|
|
414
|
+
let hasRawArrayBody = false;
|
|
415
|
+
let rawBodyData = undefined;
|
|
416
|
+
// Separate remaining parameters by location (query vs body)
|
|
417
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
418
|
+
for (const [key, value] of Object.entries(params)) {
|
|
419
|
+
if (!extractedParams.includes(key)) {
|
|
420
|
+
bodyParams[key] = value;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Validate required parameters
|
|
424
|
+
const path = this.buildPath('/databases/{database_id}', pathParams);
|
|
425
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
426
|
+
const requestPath = path === '/' ? '' : path;
|
|
427
|
+
// Report initial progress if callback provided
|
|
428
|
+
if (options?.onProgress) {
|
|
429
|
+
await options.onProgress({
|
|
430
|
+
progress: 0,
|
|
431
|
+
total: 100,
|
|
432
|
+
message: `Starting get_database request...`
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
436
|
+
const requestConfig = {};
|
|
437
|
+
if (options?.signal) {
|
|
438
|
+
requestConfig.signal = options.signal;
|
|
439
|
+
}
|
|
440
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
441
|
+
// Report completion progress if callback provided
|
|
442
|
+
if (options?.onProgress) {
|
|
443
|
+
await options.onProgress({
|
|
444
|
+
progress: 100,
|
|
445
|
+
total: 100,
|
|
446
|
+
message: `Completed get_database request`
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
const duration = Date.now() - startTime;
|
|
450
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
451
|
+
endpoint: 'get_database',
|
|
452
|
+
method: 'GET',
|
|
453
|
+
path: '/databases/{database_id}',
|
|
454
|
+
duration_ms: duration,
|
|
455
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
456
|
+
});
|
|
457
|
+
return {
|
|
458
|
+
content: [
|
|
459
|
+
{
|
|
460
|
+
type: 'text',
|
|
461
|
+
text: JSON.stringify(response.data, null, 2)
|
|
462
|
+
}
|
|
463
|
+
]
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
const duration = Date.now() - startTime;
|
|
468
|
+
// Check if error is due to cancellation
|
|
469
|
+
if (axios.isCancel(error)) {
|
|
470
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
471
|
+
endpoint: 'get_database',
|
|
472
|
+
method: 'GET',
|
|
473
|
+
path: '/databases/{database_id}',
|
|
474
|
+
duration_ms: duration
|
|
475
|
+
});
|
|
476
|
+
throw new Error('Request was cancelled');
|
|
477
|
+
}
|
|
478
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
479
|
+
endpoint: 'get_database',
|
|
480
|
+
method: 'GET',
|
|
481
|
+
path: '/databases/{database_id}',
|
|
482
|
+
duration_ms: duration,
|
|
483
|
+
error: error instanceof Error ? error.message : String(error),
|
|
484
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
485
|
+
});
|
|
486
|
+
throw new Error(`Failed to execute get_database: ${error instanceof Error ? error.message : String(error)}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/* DEBUG: endpoint={"name":"query_database","method":"POST","path":"/databases/{database_id}/query","description":"Query database pages","parameters":{"database_id":{"type":"string","required":true,"description":"Database ID to query","location":"path"},"filter":{"type":"object","required":false,"description":"Filter object to apply","location":"body"},"sorts":{"type":"array","required":false,"description":"Array of sort objects","location":"body"},"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"body"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"body","default":100}},"response_format":"json","category":"Database Operations"} */
|
|
490
|
+
async queryDatabase(params, options) {
|
|
491
|
+
const startTime = Date.now();
|
|
492
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
493
|
+
endpoint: 'query_database',
|
|
494
|
+
method: 'POST',
|
|
495
|
+
path: '/databases/{database_id}/query',
|
|
496
|
+
paramCount: Object.keys(params || {}).length,
|
|
497
|
+
paramKeys: Object.keys(params || {})
|
|
498
|
+
});
|
|
499
|
+
try {
|
|
500
|
+
// Extract and separate parameters by location: path, query, body
|
|
501
|
+
const pathTemplate = '/databases/{database_id}/query';
|
|
502
|
+
const pathParams = {};
|
|
503
|
+
const queryParams = {};
|
|
504
|
+
const bodyParams = {};
|
|
505
|
+
const extractedParams = [];
|
|
506
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
507
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
508
|
+
let match;
|
|
509
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
510
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
511
|
+
if (paramName && params[paramName] !== undefined) {
|
|
512
|
+
pathParams[paramName] = params[paramName];
|
|
513
|
+
extractedParams.push(paramName);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
// Handle standard path templates: {resourceName}
|
|
517
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
518
|
+
standardPathParams.forEach(paramTemplate => {
|
|
519
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
520
|
+
// Only process if not already handled by Google template logic
|
|
521
|
+
if (!extractedParams.includes(paramName)) {
|
|
522
|
+
if (params[paramName] !== undefined) {
|
|
523
|
+
pathParams[paramName] = params[paramName];
|
|
524
|
+
extractedParams.push(paramName);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
// Provide default values for optional path parameters
|
|
528
|
+
if (paramName === 'userId') {
|
|
529
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
530
|
+
extractedParams.push(paramName);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
// Check if any parameter has raw_array flag
|
|
536
|
+
let hasRawArrayBody = false;
|
|
537
|
+
let rawBodyData = undefined;
|
|
538
|
+
// Separate remaining parameters by location (query vs body)
|
|
539
|
+
if (params[""] !== undefined) {
|
|
540
|
+
bodyParams[""] = params[""];
|
|
541
|
+
extractedParams.push("");
|
|
542
|
+
}
|
|
543
|
+
if (params[""] !== undefined) {
|
|
544
|
+
bodyParams[""] = params[""];
|
|
545
|
+
extractedParams.push("");
|
|
546
|
+
}
|
|
547
|
+
if (params[""] !== undefined) {
|
|
548
|
+
bodyParams[""] = params[""];
|
|
549
|
+
extractedParams.push("");
|
|
550
|
+
}
|
|
551
|
+
if (params[""] !== undefined) {
|
|
552
|
+
bodyParams[""] = params[""];
|
|
553
|
+
extractedParams.push("");
|
|
554
|
+
}
|
|
555
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
556
|
+
for (const [key, value] of Object.entries(params)) {
|
|
557
|
+
if (!extractedParams.includes(key)) {
|
|
558
|
+
bodyParams[key] = value;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// Validate required parameters
|
|
562
|
+
const path = this.buildPath('/databases/{database_id}/query', pathParams);
|
|
563
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
564
|
+
const requestPath = path === '/' ? '' : path;
|
|
565
|
+
// Report initial progress if callback provided
|
|
566
|
+
if (options?.onProgress) {
|
|
567
|
+
await options.onProgress({
|
|
568
|
+
progress: 0,
|
|
569
|
+
total: 100,
|
|
570
|
+
message: `Starting query_database request...`
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
574
|
+
const requestConfig = {};
|
|
575
|
+
if (options?.signal) {
|
|
576
|
+
requestConfig.signal = options.signal;
|
|
577
|
+
}
|
|
578
|
+
const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
579
|
+
// Report completion progress if callback provided
|
|
580
|
+
if (options?.onProgress) {
|
|
581
|
+
await options.onProgress({
|
|
582
|
+
progress: 100,
|
|
583
|
+
total: 100,
|
|
584
|
+
message: `Completed query_database request`
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
const duration = Date.now() - startTime;
|
|
588
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
589
|
+
endpoint: 'query_database',
|
|
590
|
+
method: 'POST',
|
|
591
|
+
path: '/databases/{database_id}/query',
|
|
592
|
+
duration_ms: duration,
|
|
593
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
594
|
+
});
|
|
595
|
+
return {
|
|
596
|
+
content: [
|
|
597
|
+
{
|
|
598
|
+
type: 'text',
|
|
599
|
+
text: JSON.stringify(response.data, null, 2)
|
|
600
|
+
}
|
|
601
|
+
]
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
catch (error) {
|
|
605
|
+
const duration = Date.now() - startTime;
|
|
606
|
+
// Check if error is due to cancellation
|
|
607
|
+
if (axios.isCancel(error)) {
|
|
608
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
609
|
+
endpoint: 'query_database',
|
|
610
|
+
method: 'POST',
|
|
611
|
+
path: '/databases/{database_id}/query',
|
|
612
|
+
duration_ms: duration
|
|
613
|
+
});
|
|
614
|
+
throw new Error('Request was cancelled');
|
|
615
|
+
}
|
|
616
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
617
|
+
endpoint: 'query_database',
|
|
618
|
+
method: 'POST',
|
|
619
|
+
path: '/databases/{database_id}/query',
|
|
620
|
+
duration_ms: duration,
|
|
621
|
+
error: error instanceof Error ? error.message : String(error),
|
|
622
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
623
|
+
});
|
|
624
|
+
throw new Error(`Failed to execute query_database: ${error instanceof Error ? error.message : String(error)}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/* DEBUG: endpoint={"name":"create_database","method":"POST","path":"/databases","description":"Create a new database","parameters":{"parent":{"type":"object","required":true,"description":"Parent page object","location":"body"},"title":{"type":"array","required":true,"description":"Array of rich text objects for title","location":"body"},"properties":{"type":"object","required":true,"description":"Database properties schema","location":"body"},"icon":{"type":"object","required":false,"description":"Database icon object","location":"body"},"cover":{"type":"object","required":false,"description":"Database cover object","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
628
|
+
async createDatabase(params, options) {
|
|
629
|
+
const startTime = Date.now();
|
|
630
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
631
|
+
endpoint: 'create_database',
|
|
632
|
+
method: 'POST',
|
|
633
|
+
path: '/databases',
|
|
634
|
+
paramCount: Object.keys(params || {}).length,
|
|
635
|
+
paramKeys: Object.keys(params || {})
|
|
636
|
+
});
|
|
637
|
+
try {
|
|
638
|
+
// Extract and separate parameters by location: path, query, body
|
|
639
|
+
const pathTemplate = '/databases';
|
|
640
|
+
const pathParams = {};
|
|
641
|
+
const queryParams = {};
|
|
642
|
+
const bodyParams = {};
|
|
643
|
+
const extractedParams = [];
|
|
644
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
645
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
646
|
+
let match;
|
|
647
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
648
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
649
|
+
if (paramName && params[paramName] !== undefined) {
|
|
650
|
+
pathParams[paramName] = params[paramName];
|
|
651
|
+
extractedParams.push(paramName);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
// Handle standard path templates: {resourceName}
|
|
655
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
656
|
+
standardPathParams.forEach(paramTemplate => {
|
|
657
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
658
|
+
// Only process if not already handled by Google template logic
|
|
659
|
+
if (!extractedParams.includes(paramName)) {
|
|
660
|
+
if (params[paramName] !== undefined) {
|
|
661
|
+
pathParams[paramName] = params[paramName];
|
|
662
|
+
extractedParams.push(paramName);
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
// Provide default values for optional path parameters
|
|
666
|
+
if (paramName === 'userId') {
|
|
667
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
668
|
+
extractedParams.push(paramName);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
// Check if any parameter has raw_array flag
|
|
674
|
+
let hasRawArrayBody = false;
|
|
675
|
+
let rawBodyData = undefined;
|
|
676
|
+
// Separate remaining parameters by location (query vs body)
|
|
677
|
+
if (params[""] !== undefined) {
|
|
678
|
+
bodyParams[""] = params[""];
|
|
679
|
+
extractedParams.push("");
|
|
680
|
+
}
|
|
681
|
+
if (params[""] !== undefined) {
|
|
682
|
+
bodyParams[""] = params[""];
|
|
683
|
+
extractedParams.push("");
|
|
684
|
+
}
|
|
685
|
+
if (params[""] !== undefined) {
|
|
686
|
+
bodyParams[""] = params[""];
|
|
687
|
+
extractedParams.push("");
|
|
688
|
+
}
|
|
689
|
+
if (params[""] !== undefined) {
|
|
690
|
+
bodyParams[""] = params[""];
|
|
691
|
+
extractedParams.push("");
|
|
692
|
+
}
|
|
693
|
+
if (params[""] !== undefined) {
|
|
694
|
+
bodyParams[""] = params[""];
|
|
695
|
+
extractedParams.push("");
|
|
696
|
+
}
|
|
697
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
698
|
+
for (const [key, value] of Object.entries(params)) {
|
|
699
|
+
if (!extractedParams.includes(key)) {
|
|
700
|
+
bodyParams[key] = value;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
// Validate required parameters
|
|
704
|
+
const path = this.buildPath('/databases', pathParams);
|
|
705
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
706
|
+
const requestPath = path === '/' ? '' : path;
|
|
707
|
+
// Report initial progress if callback provided
|
|
708
|
+
if (options?.onProgress) {
|
|
709
|
+
await options.onProgress({
|
|
710
|
+
progress: 0,
|
|
711
|
+
total: 100,
|
|
712
|
+
message: `Starting create_database request...`
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
716
|
+
const requestConfig = {};
|
|
717
|
+
if (options?.signal) {
|
|
718
|
+
requestConfig.signal = options.signal;
|
|
719
|
+
}
|
|
720
|
+
const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
721
|
+
// Report completion progress if callback provided
|
|
722
|
+
if (options?.onProgress) {
|
|
723
|
+
await options.onProgress({
|
|
724
|
+
progress: 100,
|
|
725
|
+
total: 100,
|
|
726
|
+
message: `Completed create_database request`
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
const duration = Date.now() - startTime;
|
|
730
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
731
|
+
endpoint: 'create_database',
|
|
732
|
+
method: 'POST',
|
|
733
|
+
path: '/databases',
|
|
734
|
+
duration_ms: duration,
|
|
735
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
736
|
+
});
|
|
737
|
+
return {
|
|
738
|
+
content: [
|
|
739
|
+
{
|
|
740
|
+
type: 'text',
|
|
741
|
+
text: JSON.stringify(response.data, null, 2)
|
|
742
|
+
}
|
|
743
|
+
]
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
catch (error) {
|
|
747
|
+
const duration = Date.now() - startTime;
|
|
748
|
+
// Check if error is due to cancellation
|
|
749
|
+
if (axios.isCancel(error)) {
|
|
750
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
751
|
+
endpoint: 'create_database',
|
|
752
|
+
method: 'POST',
|
|
753
|
+
path: '/databases',
|
|
754
|
+
duration_ms: duration
|
|
755
|
+
});
|
|
756
|
+
throw new Error('Request was cancelled');
|
|
757
|
+
}
|
|
758
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
759
|
+
endpoint: 'create_database',
|
|
760
|
+
method: 'POST',
|
|
761
|
+
path: '/databases',
|
|
762
|
+
duration_ms: duration,
|
|
763
|
+
error: error instanceof Error ? error.message : String(error),
|
|
764
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
765
|
+
});
|
|
766
|
+
throw new Error(`Failed to execute create_database: ${error instanceof Error ? error.message : String(error)}`);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
/* DEBUG: endpoint={"name":"update_database","method":"PATCH","path":"/databases/{database_id}","description":"Update database properties","parameters":{"database_id":{"type":"string","required":true,"description":"Database ID to update","location":"path"},"title":{"type":"array","required":false,"description":"Array of rich text objects for title","location":"body"},"properties":{"type":"object","required":false,"description":"Database properties to update","location":"body"},"icon":{"type":"object","required":false,"description":"Database icon object","location":"body"},"cover":{"type":"object","required":false,"description":"Database cover object","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
770
|
+
async updateDatabase(params, options) {
|
|
771
|
+
const startTime = Date.now();
|
|
772
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
773
|
+
endpoint: 'update_database',
|
|
774
|
+
method: 'PATCH',
|
|
775
|
+
path: '/databases/{database_id}',
|
|
776
|
+
paramCount: Object.keys(params || {}).length,
|
|
777
|
+
paramKeys: Object.keys(params || {})
|
|
778
|
+
});
|
|
779
|
+
try {
|
|
780
|
+
// Extract and separate parameters by location: path, query, body
|
|
781
|
+
const pathTemplate = '/databases/{database_id}';
|
|
782
|
+
const pathParams = {};
|
|
783
|
+
const queryParams = {};
|
|
784
|
+
const bodyParams = {};
|
|
785
|
+
const extractedParams = [];
|
|
786
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
787
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
788
|
+
let match;
|
|
789
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
790
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
791
|
+
if (paramName && params[paramName] !== undefined) {
|
|
792
|
+
pathParams[paramName] = params[paramName];
|
|
793
|
+
extractedParams.push(paramName);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
// Handle standard path templates: {resourceName}
|
|
797
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
798
|
+
standardPathParams.forEach(paramTemplate => {
|
|
799
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
800
|
+
// Only process if not already handled by Google template logic
|
|
801
|
+
if (!extractedParams.includes(paramName)) {
|
|
802
|
+
if (params[paramName] !== undefined) {
|
|
803
|
+
pathParams[paramName] = params[paramName];
|
|
804
|
+
extractedParams.push(paramName);
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
// Provide default values for optional path parameters
|
|
808
|
+
if (paramName === 'userId') {
|
|
809
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
810
|
+
extractedParams.push(paramName);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
// Check if any parameter has raw_array flag
|
|
816
|
+
let hasRawArrayBody = false;
|
|
817
|
+
let rawBodyData = undefined;
|
|
818
|
+
// Separate remaining parameters by location (query vs body)
|
|
819
|
+
if (params[""] !== undefined) {
|
|
820
|
+
bodyParams[""] = params[""];
|
|
821
|
+
extractedParams.push("");
|
|
822
|
+
}
|
|
823
|
+
if (params[""] !== undefined) {
|
|
824
|
+
bodyParams[""] = params[""];
|
|
825
|
+
extractedParams.push("");
|
|
826
|
+
}
|
|
827
|
+
if (params[""] !== undefined) {
|
|
828
|
+
bodyParams[""] = params[""];
|
|
829
|
+
extractedParams.push("");
|
|
830
|
+
}
|
|
831
|
+
if (params[""] !== undefined) {
|
|
832
|
+
bodyParams[""] = params[""];
|
|
833
|
+
extractedParams.push("");
|
|
834
|
+
}
|
|
835
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
836
|
+
for (const [key, value] of Object.entries(params)) {
|
|
837
|
+
if (!extractedParams.includes(key)) {
|
|
838
|
+
bodyParams[key] = value;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
// Validate required parameters
|
|
842
|
+
const path = this.buildPath('/databases/{database_id}', pathParams);
|
|
843
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
844
|
+
const requestPath = path === '/' ? '' : path;
|
|
845
|
+
// Report initial progress if callback provided
|
|
846
|
+
if (options?.onProgress) {
|
|
847
|
+
await options.onProgress({
|
|
848
|
+
progress: 0,
|
|
849
|
+
total: 100,
|
|
850
|
+
message: `Starting update_database request...`
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
854
|
+
const requestConfig = {};
|
|
855
|
+
if (options?.signal) {
|
|
856
|
+
requestConfig.signal = options.signal;
|
|
857
|
+
}
|
|
858
|
+
const response = await this.httpClient.patch(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
859
|
+
// Report completion progress if callback provided
|
|
860
|
+
if (options?.onProgress) {
|
|
861
|
+
await options.onProgress({
|
|
862
|
+
progress: 100,
|
|
863
|
+
total: 100,
|
|
864
|
+
message: `Completed update_database request`
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
const duration = Date.now() - startTime;
|
|
868
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
869
|
+
endpoint: 'update_database',
|
|
870
|
+
method: 'PATCH',
|
|
871
|
+
path: '/databases/{database_id}',
|
|
872
|
+
duration_ms: duration,
|
|
873
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
874
|
+
});
|
|
875
|
+
return {
|
|
876
|
+
content: [
|
|
877
|
+
{
|
|
878
|
+
type: 'text',
|
|
879
|
+
text: JSON.stringify(response.data, null, 2)
|
|
880
|
+
}
|
|
881
|
+
]
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
catch (error) {
|
|
885
|
+
const duration = Date.now() - startTime;
|
|
886
|
+
// Check if error is due to cancellation
|
|
887
|
+
if (axios.isCancel(error)) {
|
|
888
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
889
|
+
endpoint: 'update_database',
|
|
890
|
+
method: 'PATCH',
|
|
891
|
+
path: '/databases/{database_id}',
|
|
892
|
+
duration_ms: duration
|
|
893
|
+
});
|
|
894
|
+
throw new Error('Request was cancelled');
|
|
895
|
+
}
|
|
896
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
897
|
+
endpoint: 'update_database',
|
|
898
|
+
method: 'PATCH',
|
|
899
|
+
path: '/databases/{database_id}',
|
|
900
|
+
duration_ms: duration,
|
|
901
|
+
error: error instanceof Error ? error.message : String(error),
|
|
902
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
903
|
+
});
|
|
904
|
+
throw new Error(`Failed to execute update_database: ${error instanceof Error ? error.message : String(error)}`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
/* DEBUG: endpoint={"name":"get_page","method":"GET","path":"/pages/{page_id}","description":"Get page by ID","parameters":{"page_id":{"type":"string","required":true,"description":"Page ID to fetch","location":"path"}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
908
|
+
async getPage(params, options) {
|
|
909
|
+
const startTime = Date.now();
|
|
910
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
911
|
+
endpoint: 'get_page',
|
|
912
|
+
method: 'GET',
|
|
913
|
+
path: '/pages/{page_id}',
|
|
914
|
+
paramCount: Object.keys(params || {}).length,
|
|
915
|
+
paramKeys: Object.keys(params || {})
|
|
916
|
+
});
|
|
917
|
+
try {
|
|
918
|
+
// Extract and separate parameters by location: path, query, body
|
|
919
|
+
const pathTemplate = '/pages/{page_id}';
|
|
920
|
+
const pathParams = {};
|
|
921
|
+
const queryParams = {};
|
|
922
|
+
const bodyParams = {};
|
|
923
|
+
const extractedParams = [];
|
|
924
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
925
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
926
|
+
let match;
|
|
927
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
928
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
929
|
+
if (paramName && params[paramName] !== undefined) {
|
|
930
|
+
pathParams[paramName] = params[paramName];
|
|
931
|
+
extractedParams.push(paramName);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
// Handle standard path templates: {resourceName}
|
|
935
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
936
|
+
standardPathParams.forEach(paramTemplate => {
|
|
937
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
938
|
+
// Only process if not already handled by Google template logic
|
|
939
|
+
if (!extractedParams.includes(paramName)) {
|
|
940
|
+
if (params[paramName] !== undefined) {
|
|
941
|
+
pathParams[paramName] = params[paramName];
|
|
942
|
+
extractedParams.push(paramName);
|
|
943
|
+
}
|
|
944
|
+
else {
|
|
945
|
+
// Provide default values for optional path parameters
|
|
946
|
+
if (paramName === 'userId') {
|
|
947
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
948
|
+
extractedParams.push(paramName);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
// Check if any parameter has raw_array flag
|
|
954
|
+
let hasRawArrayBody = false;
|
|
955
|
+
let rawBodyData = undefined;
|
|
956
|
+
// Separate remaining parameters by location (query vs body)
|
|
957
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
958
|
+
for (const [key, value] of Object.entries(params)) {
|
|
959
|
+
if (!extractedParams.includes(key)) {
|
|
960
|
+
bodyParams[key] = value;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
// Validate required parameters
|
|
964
|
+
const path = this.buildPath('/pages/{page_id}', pathParams);
|
|
965
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
966
|
+
const requestPath = path === '/' ? '' : path;
|
|
967
|
+
// Report initial progress if callback provided
|
|
968
|
+
if (options?.onProgress) {
|
|
969
|
+
await options.onProgress({
|
|
970
|
+
progress: 0,
|
|
971
|
+
total: 100,
|
|
972
|
+
message: `Starting get_page request...`
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
976
|
+
const requestConfig = {};
|
|
977
|
+
if (options?.signal) {
|
|
978
|
+
requestConfig.signal = options.signal;
|
|
979
|
+
}
|
|
980
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
981
|
+
// Report completion progress if callback provided
|
|
982
|
+
if (options?.onProgress) {
|
|
983
|
+
await options.onProgress({
|
|
984
|
+
progress: 100,
|
|
985
|
+
total: 100,
|
|
986
|
+
message: `Completed get_page request`
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
const duration = Date.now() - startTime;
|
|
990
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
991
|
+
endpoint: 'get_page',
|
|
992
|
+
method: 'GET',
|
|
993
|
+
path: '/pages/{page_id}',
|
|
994
|
+
duration_ms: duration,
|
|
995
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
996
|
+
});
|
|
997
|
+
return {
|
|
998
|
+
content: [
|
|
999
|
+
{
|
|
1000
|
+
type: 'text',
|
|
1001
|
+
text: JSON.stringify(response.data, null, 2)
|
|
1002
|
+
}
|
|
1003
|
+
]
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
catch (error) {
|
|
1007
|
+
const duration = Date.now() - startTime;
|
|
1008
|
+
// Check if error is due to cancellation
|
|
1009
|
+
if (axios.isCancel(error)) {
|
|
1010
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
1011
|
+
endpoint: 'get_page',
|
|
1012
|
+
method: 'GET',
|
|
1013
|
+
path: '/pages/{page_id}',
|
|
1014
|
+
duration_ms: duration
|
|
1015
|
+
});
|
|
1016
|
+
throw new Error('Request was cancelled');
|
|
1017
|
+
}
|
|
1018
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
1019
|
+
endpoint: 'get_page',
|
|
1020
|
+
method: 'GET',
|
|
1021
|
+
path: '/pages/{page_id}',
|
|
1022
|
+
duration_ms: duration,
|
|
1023
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1024
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
1025
|
+
});
|
|
1026
|
+
throw new Error(`Failed to execute get_page: ${error instanceof Error ? error.message : String(error)}`);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/* DEBUG: endpoint={"name":"create_page","method":"POST","path":"/pages","description":"Create a new page. Note: Creating pages directly in workspace root requires special permissions - use database or page parents instead.","parameters":{"parent":{"type":"object","required":true,"description":"Parent object (database or page)","location":"body"},"properties":{"type":"object","required":false,"description":"Page properties (required for database pages)","location":"body"},"children":{"type":"array","required":false,"description":"Array of block objects for page content","location":"body"},"icon":{"type":"object","required":false,"description":"Page icon object","location":"body"},"cover":{"type":"object","required":false,"description":"Page cover object","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
1030
|
+
async createPage(params, options) {
|
|
1031
|
+
const startTime = Date.now();
|
|
1032
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
1033
|
+
endpoint: 'create_page',
|
|
1034
|
+
method: 'POST',
|
|
1035
|
+
path: '/pages',
|
|
1036
|
+
paramCount: Object.keys(params || {}).length,
|
|
1037
|
+
paramKeys: Object.keys(params || {})
|
|
1038
|
+
});
|
|
1039
|
+
try {
|
|
1040
|
+
// Extract and separate parameters by location: path, query, body
|
|
1041
|
+
const pathTemplate = '/pages';
|
|
1042
|
+
const pathParams = {};
|
|
1043
|
+
const queryParams = {};
|
|
1044
|
+
const bodyParams = {};
|
|
1045
|
+
const extractedParams = [];
|
|
1046
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
1047
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
1048
|
+
let match;
|
|
1049
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
1050
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
1051
|
+
if (paramName && params[paramName] !== undefined) {
|
|
1052
|
+
pathParams[paramName] = params[paramName];
|
|
1053
|
+
extractedParams.push(paramName);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
// Handle standard path templates: {resourceName}
|
|
1057
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
1058
|
+
standardPathParams.forEach(paramTemplate => {
|
|
1059
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
1060
|
+
// Only process if not already handled by Google template logic
|
|
1061
|
+
if (!extractedParams.includes(paramName)) {
|
|
1062
|
+
if (params[paramName] !== undefined) {
|
|
1063
|
+
pathParams[paramName] = params[paramName];
|
|
1064
|
+
extractedParams.push(paramName);
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
// Provide default values for optional path parameters
|
|
1068
|
+
if (paramName === 'userId') {
|
|
1069
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
1070
|
+
extractedParams.push(paramName);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
// Check if any parameter has raw_array flag
|
|
1076
|
+
let hasRawArrayBody = false;
|
|
1077
|
+
let rawBodyData = undefined;
|
|
1078
|
+
// Separate remaining parameters by location (query vs body)
|
|
1079
|
+
if (params[""] !== undefined) {
|
|
1080
|
+
bodyParams[""] = params[""];
|
|
1081
|
+
extractedParams.push("");
|
|
1082
|
+
}
|
|
1083
|
+
if (params[""] !== undefined) {
|
|
1084
|
+
bodyParams[""] = params[""];
|
|
1085
|
+
extractedParams.push("");
|
|
1086
|
+
}
|
|
1087
|
+
if (params[""] !== undefined) {
|
|
1088
|
+
bodyParams[""] = params[""];
|
|
1089
|
+
extractedParams.push("");
|
|
1090
|
+
}
|
|
1091
|
+
if (params[""] !== undefined) {
|
|
1092
|
+
bodyParams[""] = params[""];
|
|
1093
|
+
extractedParams.push("");
|
|
1094
|
+
}
|
|
1095
|
+
if (params[""] !== undefined) {
|
|
1096
|
+
bodyParams[""] = params[""];
|
|
1097
|
+
extractedParams.push("");
|
|
1098
|
+
}
|
|
1099
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
1100
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1101
|
+
if (!extractedParams.includes(key)) {
|
|
1102
|
+
bodyParams[key] = value;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
// Validate required parameters
|
|
1106
|
+
const path = this.buildPath('/pages', pathParams);
|
|
1107
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
1108
|
+
const requestPath = path === '/' ? '' : path;
|
|
1109
|
+
// Report initial progress if callback provided
|
|
1110
|
+
if (options?.onProgress) {
|
|
1111
|
+
await options.onProgress({
|
|
1112
|
+
progress: 0,
|
|
1113
|
+
total: 100,
|
|
1114
|
+
message: `Starting create_page request...`
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
1118
|
+
const requestConfig = {};
|
|
1119
|
+
if (options?.signal) {
|
|
1120
|
+
requestConfig.signal = options.signal;
|
|
1121
|
+
}
|
|
1122
|
+
const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
1123
|
+
// Report completion progress if callback provided
|
|
1124
|
+
if (options?.onProgress) {
|
|
1125
|
+
await options.onProgress({
|
|
1126
|
+
progress: 100,
|
|
1127
|
+
total: 100,
|
|
1128
|
+
message: `Completed create_page request`
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
const duration = Date.now() - startTime;
|
|
1132
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
1133
|
+
endpoint: 'create_page',
|
|
1134
|
+
method: 'POST',
|
|
1135
|
+
path: '/pages',
|
|
1136
|
+
duration_ms: duration,
|
|
1137
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
1138
|
+
});
|
|
1139
|
+
return {
|
|
1140
|
+
content: [
|
|
1141
|
+
{
|
|
1142
|
+
type: 'text',
|
|
1143
|
+
text: JSON.stringify(response.data, null, 2)
|
|
1144
|
+
}
|
|
1145
|
+
]
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
catch (error) {
|
|
1149
|
+
const duration = Date.now() - startTime;
|
|
1150
|
+
// Check if error is due to cancellation
|
|
1151
|
+
if (axios.isCancel(error)) {
|
|
1152
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
1153
|
+
endpoint: 'create_page',
|
|
1154
|
+
method: 'POST',
|
|
1155
|
+
path: '/pages',
|
|
1156
|
+
duration_ms: duration
|
|
1157
|
+
});
|
|
1158
|
+
throw new Error('Request was cancelled');
|
|
1159
|
+
}
|
|
1160
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
1161
|
+
endpoint: 'create_page',
|
|
1162
|
+
method: 'POST',
|
|
1163
|
+
path: '/pages',
|
|
1164
|
+
duration_ms: duration,
|
|
1165
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1166
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
1167
|
+
});
|
|
1168
|
+
throw new Error(`Failed to execute create_page: ${error instanceof Error ? error.message : String(error)}`);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
/* DEBUG: endpoint={"name":"update_page","method":"PATCH","path":"/pages/{page_id}","description":"Update page properties","parameters":{"page_id":{"type":"string","required":true,"description":"Page ID to update","location":"path"},"properties":{"type":"object","required":false,"description":"Page properties to update","location":"body"},"archived":{"type":"boolean","required":false,"description":"Archive or unarchive the page","location":"body"},"icon":{"type":"object","required":false,"description":"Page icon object","location":"body"},"cover":{"type":"object","required":false,"description":"Page cover object","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
1172
|
+
async updatePage(params, options) {
|
|
1173
|
+
const startTime = Date.now();
|
|
1174
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
1175
|
+
endpoint: 'update_page',
|
|
1176
|
+
method: 'PATCH',
|
|
1177
|
+
path: '/pages/{page_id}',
|
|
1178
|
+
paramCount: Object.keys(params || {}).length,
|
|
1179
|
+
paramKeys: Object.keys(params || {})
|
|
1180
|
+
});
|
|
1181
|
+
try {
|
|
1182
|
+
// Extract and separate parameters by location: path, query, body
|
|
1183
|
+
const pathTemplate = '/pages/{page_id}';
|
|
1184
|
+
const pathParams = {};
|
|
1185
|
+
const queryParams = {};
|
|
1186
|
+
const bodyParams = {};
|
|
1187
|
+
const extractedParams = [];
|
|
1188
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
1189
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
1190
|
+
let match;
|
|
1191
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
1192
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
1193
|
+
if (paramName && params[paramName] !== undefined) {
|
|
1194
|
+
pathParams[paramName] = params[paramName];
|
|
1195
|
+
extractedParams.push(paramName);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
// Handle standard path templates: {resourceName}
|
|
1199
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
1200
|
+
standardPathParams.forEach(paramTemplate => {
|
|
1201
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
1202
|
+
// Only process if not already handled by Google template logic
|
|
1203
|
+
if (!extractedParams.includes(paramName)) {
|
|
1204
|
+
if (params[paramName] !== undefined) {
|
|
1205
|
+
pathParams[paramName] = params[paramName];
|
|
1206
|
+
extractedParams.push(paramName);
|
|
1207
|
+
}
|
|
1208
|
+
else {
|
|
1209
|
+
// Provide default values for optional path parameters
|
|
1210
|
+
if (paramName === 'userId') {
|
|
1211
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
1212
|
+
extractedParams.push(paramName);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
// Check if any parameter has raw_array flag
|
|
1218
|
+
let hasRawArrayBody = false;
|
|
1219
|
+
let rawBodyData = undefined;
|
|
1220
|
+
// Separate remaining parameters by location (query vs body)
|
|
1221
|
+
if (params[""] !== undefined) {
|
|
1222
|
+
bodyParams[""] = params[""];
|
|
1223
|
+
extractedParams.push("");
|
|
1224
|
+
}
|
|
1225
|
+
if (params[""] !== undefined) {
|
|
1226
|
+
bodyParams[""] = params[""];
|
|
1227
|
+
extractedParams.push("");
|
|
1228
|
+
}
|
|
1229
|
+
if (params[""] !== undefined) {
|
|
1230
|
+
bodyParams[""] = params[""];
|
|
1231
|
+
extractedParams.push("");
|
|
1232
|
+
}
|
|
1233
|
+
if (params[""] !== undefined) {
|
|
1234
|
+
bodyParams[""] = params[""];
|
|
1235
|
+
extractedParams.push("");
|
|
1236
|
+
}
|
|
1237
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
1238
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1239
|
+
if (!extractedParams.includes(key)) {
|
|
1240
|
+
bodyParams[key] = value;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
// Validate required parameters
|
|
1244
|
+
const path = this.buildPath('/pages/{page_id}', pathParams);
|
|
1245
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
1246
|
+
const requestPath = path === '/' ? '' : path;
|
|
1247
|
+
// Report initial progress if callback provided
|
|
1248
|
+
if (options?.onProgress) {
|
|
1249
|
+
await options.onProgress({
|
|
1250
|
+
progress: 0,
|
|
1251
|
+
total: 100,
|
|
1252
|
+
message: `Starting update_page request...`
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
1256
|
+
const requestConfig = {};
|
|
1257
|
+
if (options?.signal) {
|
|
1258
|
+
requestConfig.signal = options.signal;
|
|
1259
|
+
}
|
|
1260
|
+
const response = await this.httpClient.patch(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
1261
|
+
// Report completion progress if callback provided
|
|
1262
|
+
if (options?.onProgress) {
|
|
1263
|
+
await options.onProgress({
|
|
1264
|
+
progress: 100,
|
|
1265
|
+
total: 100,
|
|
1266
|
+
message: `Completed update_page request`
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
const duration = Date.now() - startTime;
|
|
1270
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
1271
|
+
endpoint: 'update_page',
|
|
1272
|
+
method: 'PATCH',
|
|
1273
|
+
path: '/pages/{page_id}',
|
|
1274
|
+
duration_ms: duration,
|
|
1275
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
1276
|
+
});
|
|
1277
|
+
return {
|
|
1278
|
+
content: [
|
|
1279
|
+
{
|
|
1280
|
+
type: 'text',
|
|
1281
|
+
text: JSON.stringify(response.data, null, 2)
|
|
1282
|
+
}
|
|
1283
|
+
]
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
catch (error) {
|
|
1287
|
+
const duration = Date.now() - startTime;
|
|
1288
|
+
// Check if error is due to cancellation
|
|
1289
|
+
if (axios.isCancel(error)) {
|
|
1290
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
1291
|
+
endpoint: 'update_page',
|
|
1292
|
+
method: 'PATCH',
|
|
1293
|
+
path: '/pages/{page_id}',
|
|
1294
|
+
duration_ms: duration
|
|
1295
|
+
});
|
|
1296
|
+
throw new Error('Request was cancelled');
|
|
1297
|
+
}
|
|
1298
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
1299
|
+
endpoint: 'update_page',
|
|
1300
|
+
method: 'PATCH',
|
|
1301
|
+
path: '/pages/{page_id}',
|
|
1302
|
+
duration_ms: duration,
|
|
1303
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1304
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
1305
|
+
});
|
|
1306
|
+
throw new Error(`Failed to execute update_page: ${error instanceof Error ? error.message : String(error)}`);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
/* DEBUG: endpoint={"name":"get_page_property","method":"GET","path":"/pages/{page_id}/properties/{property_id}","description":"Get page property by ID","parameters":{"page_id":{"type":"string","required":true,"description":"Page ID containing the property","location":"path"},"property_id":{"type":"string","required":true,"description":"Property ID to fetch","location":"path"},"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"query"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"query","default":100}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
1310
|
+
async getPageProperty(params, options) {
|
|
1311
|
+
const startTime = Date.now();
|
|
1312
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
1313
|
+
endpoint: 'get_page_property',
|
|
1314
|
+
method: 'GET',
|
|
1315
|
+
path: '/pages/{page_id}/properties/{property_id}',
|
|
1316
|
+
paramCount: Object.keys(params || {}).length,
|
|
1317
|
+
paramKeys: Object.keys(params || {})
|
|
1318
|
+
});
|
|
1319
|
+
try {
|
|
1320
|
+
// Extract and separate parameters by location: path, query, body
|
|
1321
|
+
const pathTemplate = '/pages/{page_id}/properties/{property_id}';
|
|
1322
|
+
const pathParams = {};
|
|
1323
|
+
const queryParams = {};
|
|
1324
|
+
const bodyParams = {};
|
|
1325
|
+
const extractedParams = [];
|
|
1326
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
1327
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
1328
|
+
let match;
|
|
1329
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
1330
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
1331
|
+
if (paramName && params[paramName] !== undefined) {
|
|
1332
|
+
pathParams[paramName] = params[paramName];
|
|
1333
|
+
extractedParams.push(paramName);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
// Handle standard path templates: {resourceName}
|
|
1337
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
1338
|
+
standardPathParams.forEach(paramTemplate => {
|
|
1339
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
1340
|
+
// Only process if not already handled by Google template logic
|
|
1341
|
+
if (!extractedParams.includes(paramName)) {
|
|
1342
|
+
if (params[paramName] !== undefined) {
|
|
1343
|
+
pathParams[paramName] = params[paramName];
|
|
1344
|
+
extractedParams.push(paramName);
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
// Provide default values for optional path parameters
|
|
1348
|
+
if (paramName === 'userId') {
|
|
1349
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
1350
|
+
extractedParams.push(paramName);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
// Check if any parameter has raw_array flag
|
|
1356
|
+
let hasRawArrayBody = false;
|
|
1357
|
+
let rawBodyData = undefined;
|
|
1358
|
+
// Separate remaining parameters by location (query vs body)
|
|
1359
|
+
if (params[""] !== undefined) {
|
|
1360
|
+
queryParams[""] = params[""];
|
|
1361
|
+
extractedParams.push("");
|
|
1362
|
+
}
|
|
1363
|
+
if (params[""] !== undefined) {
|
|
1364
|
+
queryParams[""] = params[""];
|
|
1365
|
+
extractedParams.push("");
|
|
1366
|
+
}
|
|
1367
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
1368
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1369
|
+
if (!extractedParams.includes(key)) {
|
|
1370
|
+
bodyParams[key] = value;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
// Validate required parameters
|
|
1374
|
+
const path = this.buildPath('/pages/{page_id}/properties/{property_id}', pathParams);
|
|
1375
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
1376
|
+
const requestPath = path === '/' ? '' : path;
|
|
1377
|
+
// Report initial progress if callback provided
|
|
1378
|
+
if (options?.onProgress) {
|
|
1379
|
+
await options.onProgress({
|
|
1380
|
+
progress: 0,
|
|
1381
|
+
total: 100,
|
|
1382
|
+
message: `Starting get_page_property request...`
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
1386
|
+
const requestConfig = {};
|
|
1387
|
+
if (options?.signal) {
|
|
1388
|
+
requestConfig.signal = options.signal;
|
|
1389
|
+
}
|
|
1390
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
1391
|
+
// Report completion progress if callback provided
|
|
1392
|
+
if (options?.onProgress) {
|
|
1393
|
+
await options.onProgress({
|
|
1394
|
+
progress: 100,
|
|
1395
|
+
total: 100,
|
|
1396
|
+
message: `Completed get_page_property request`
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
const duration = Date.now() - startTime;
|
|
1400
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
1401
|
+
endpoint: 'get_page_property',
|
|
1402
|
+
method: 'GET',
|
|
1403
|
+
path: '/pages/{page_id}/properties/{property_id}',
|
|
1404
|
+
duration_ms: duration,
|
|
1405
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
1406
|
+
});
|
|
1407
|
+
return {
|
|
1408
|
+
content: [
|
|
1409
|
+
{
|
|
1410
|
+
type: 'text',
|
|
1411
|
+
text: JSON.stringify(response.data, null, 2)
|
|
1412
|
+
}
|
|
1413
|
+
]
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
catch (error) {
|
|
1417
|
+
const duration = Date.now() - startTime;
|
|
1418
|
+
// Check if error is due to cancellation
|
|
1419
|
+
if (axios.isCancel(error)) {
|
|
1420
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
1421
|
+
endpoint: 'get_page_property',
|
|
1422
|
+
method: 'GET',
|
|
1423
|
+
path: '/pages/{page_id}/properties/{property_id}',
|
|
1424
|
+
duration_ms: duration
|
|
1425
|
+
});
|
|
1426
|
+
throw new Error('Request was cancelled');
|
|
1427
|
+
}
|
|
1428
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
1429
|
+
endpoint: 'get_page_property',
|
|
1430
|
+
method: 'GET',
|
|
1431
|
+
path: '/pages/{page_id}/properties/{property_id}',
|
|
1432
|
+
duration_ms: duration,
|
|
1433
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1434
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
1435
|
+
});
|
|
1436
|
+
throw new Error(`Failed to execute get_page_property: ${error instanceof Error ? error.message : String(error)}`);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
/* DEBUG: endpoint={"name":"get_block_children","method":"GET","path":"/blocks/{block_id}/children","description":"Get block children","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to get children for","location":"path"},"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"query"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"query","default":100}},"response_format":"json","category":"Block Content"} */
|
|
1440
|
+
async getBlockChildren(params, options) {
|
|
1441
|
+
const startTime = Date.now();
|
|
1442
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
1443
|
+
endpoint: 'get_block_children',
|
|
1444
|
+
method: 'GET',
|
|
1445
|
+
path: '/blocks/{block_id}/children',
|
|
1446
|
+
paramCount: Object.keys(params || {}).length,
|
|
1447
|
+
paramKeys: Object.keys(params || {})
|
|
1448
|
+
});
|
|
1449
|
+
try {
|
|
1450
|
+
// Extract and separate parameters by location: path, query, body
|
|
1451
|
+
const pathTemplate = '/blocks/{block_id}/children';
|
|
1452
|
+
const pathParams = {};
|
|
1453
|
+
const queryParams = {};
|
|
1454
|
+
const bodyParams = {};
|
|
1455
|
+
const extractedParams = [];
|
|
1456
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
1457
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
1458
|
+
let match;
|
|
1459
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
1460
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
1461
|
+
if (paramName && params[paramName] !== undefined) {
|
|
1462
|
+
pathParams[paramName] = params[paramName];
|
|
1463
|
+
extractedParams.push(paramName);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
// Handle standard path templates: {resourceName}
|
|
1467
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
1468
|
+
standardPathParams.forEach(paramTemplate => {
|
|
1469
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
1470
|
+
// Only process if not already handled by Google template logic
|
|
1471
|
+
if (!extractedParams.includes(paramName)) {
|
|
1472
|
+
if (params[paramName] !== undefined) {
|
|
1473
|
+
pathParams[paramName] = params[paramName];
|
|
1474
|
+
extractedParams.push(paramName);
|
|
1475
|
+
}
|
|
1476
|
+
else {
|
|
1477
|
+
// Provide default values for optional path parameters
|
|
1478
|
+
if (paramName === 'userId') {
|
|
1479
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
1480
|
+
extractedParams.push(paramName);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
// Check if any parameter has raw_array flag
|
|
1486
|
+
let hasRawArrayBody = false;
|
|
1487
|
+
let rawBodyData = undefined;
|
|
1488
|
+
// Separate remaining parameters by location (query vs body)
|
|
1489
|
+
if (params[""] !== undefined) {
|
|
1490
|
+
queryParams[""] = params[""];
|
|
1491
|
+
extractedParams.push("");
|
|
1492
|
+
}
|
|
1493
|
+
if (params[""] !== undefined) {
|
|
1494
|
+
queryParams[""] = params[""];
|
|
1495
|
+
extractedParams.push("");
|
|
1496
|
+
}
|
|
1497
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
1498
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1499
|
+
if (!extractedParams.includes(key)) {
|
|
1500
|
+
bodyParams[key] = value;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
// Validate required parameters
|
|
1504
|
+
const path = this.buildPath('/blocks/{block_id}/children', pathParams);
|
|
1505
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
1506
|
+
const requestPath = path === '/' ? '' : path;
|
|
1507
|
+
// Report initial progress if callback provided
|
|
1508
|
+
if (options?.onProgress) {
|
|
1509
|
+
await options.onProgress({
|
|
1510
|
+
progress: 0,
|
|
1511
|
+
total: 100,
|
|
1512
|
+
message: `Starting get_block_children request...`
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
1516
|
+
const requestConfig = {};
|
|
1517
|
+
if (options?.signal) {
|
|
1518
|
+
requestConfig.signal = options.signal;
|
|
1519
|
+
}
|
|
1520
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
1521
|
+
// Report completion progress if callback provided
|
|
1522
|
+
if (options?.onProgress) {
|
|
1523
|
+
await options.onProgress({
|
|
1524
|
+
progress: 100,
|
|
1525
|
+
total: 100,
|
|
1526
|
+
message: `Completed get_block_children request`
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
const duration = Date.now() - startTime;
|
|
1530
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
1531
|
+
endpoint: 'get_block_children',
|
|
1532
|
+
method: 'GET',
|
|
1533
|
+
path: '/blocks/{block_id}/children',
|
|
1534
|
+
duration_ms: duration,
|
|
1535
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
1536
|
+
});
|
|
1537
|
+
return {
|
|
1538
|
+
content: [
|
|
1539
|
+
{
|
|
1540
|
+
type: 'text',
|
|
1541
|
+
text: JSON.stringify(response.data, null, 2)
|
|
1542
|
+
}
|
|
1543
|
+
]
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
catch (error) {
|
|
1547
|
+
const duration = Date.now() - startTime;
|
|
1548
|
+
// Check if error is due to cancellation
|
|
1549
|
+
if (axios.isCancel(error)) {
|
|
1550
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
1551
|
+
endpoint: 'get_block_children',
|
|
1552
|
+
method: 'GET',
|
|
1553
|
+
path: '/blocks/{block_id}/children',
|
|
1554
|
+
duration_ms: duration
|
|
1555
|
+
});
|
|
1556
|
+
throw new Error('Request was cancelled');
|
|
1557
|
+
}
|
|
1558
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
1559
|
+
endpoint: 'get_block_children',
|
|
1560
|
+
method: 'GET',
|
|
1561
|
+
path: '/blocks/{block_id}/children',
|
|
1562
|
+
duration_ms: duration,
|
|
1563
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1564
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
1565
|
+
});
|
|
1566
|
+
throw new Error(`Failed to execute get_block_children: ${error instanceof Error ? error.message : String(error)}`);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
/* DEBUG: endpoint={"name":"append_block_children","method":"PATCH","path":"/blocks/{block_id}/children","description":"Append blocks to a parent block","parameters":{"block_id":{"type":"string","required":true,"description":"Parent block ID","location":"path"},"children":{"type":"array","required":true,"description":"Array of block objects to append","location":"body"}},"response_format":"json","category":"Block Content"} */
|
|
1570
|
+
async appendBlockChildren(params, options) {
|
|
1571
|
+
const startTime = Date.now();
|
|
1572
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
1573
|
+
endpoint: 'append_block_children',
|
|
1574
|
+
method: 'PATCH',
|
|
1575
|
+
path: '/blocks/{block_id}/children',
|
|
1576
|
+
paramCount: Object.keys(params || {}).length,
|
|
1577
|
+
paramKeys: Object.keys(params || {})
|
|
1578
|
+
});
|
|
1579
|
+
try {
|
|
1580
|
+
// Extract and separate parameters by location: path, query, body
|
|
1581
|
+
const pathTemplate = '/blocks/{block_id}/children';
|
|
1582
|
+
const pathParams = {};
|
|
1583
|
+
const queryParams = {};
|
|
1584
|
+
const bodyParams = {};
|
|
1585
|
+
const extractedParams = [];
|
|
1586
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
1587
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
1588
|
+
let match;
|
|
1589
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
1590
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
1591
|
+
if (paramName && params[paramName] !== undefined) {
|
|
1592
|
+
pathParams[paramName] = params[paramName];
|
|
1593
|
+
extractedParams.push(paramName);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
// Handle standard path templates: {resourceName}
|
|
1597
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
1598
|
+
standardPathParams.forEach(paramTemplate => {
|
|
1599
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
1600
|
+
// Only process if not already handled by Google template logic
|
|
1601
|
+
if (!extractedParams.includes(paramName)) {
|
|
1602
|
+
if (params[paramName] !== undefined) {
|
|
1603
|
+
pathParams[paramName] = params[paramName];
|
|
1604
|
+
extractedParams.push(paramName);
|
|
1605
|
+
}
|
|
1606
|
+
else {
|
|
1607
|
+
// Provide default values for optional path parameters
|
|
1608
|
+
if (paramName === 'userId') {
|
|
1609
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
1610
|
+
extractedParams.push(paramName);
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
// Check if any parameter has raw_array flag
|
|
1616
|
+
let hasRawArrayBody = false;
|
|
1617
|
+
let rawBodyData = undefined;
|
|
1618
|
+
// Separate remaining parameters by location (query vs body)
|
|
1619
|
+
if (params[""] !== undefined) {
|
|
1620
|
+
bodyParams[""] = params[""];
|
|
1621
|
+
extractedParams.push("");
|
|
1622
|
+
}
|
|
1623
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
1624
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1625
|
+
if (!extractedParams.includes(key)) {
|
|
1626
|
+
bodyParams[key] = value;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
// Validate required parameters
|
|
1630
|
+
const path = this.buildPath('/blocks/{block_id}/children', pathParams);
|
|
1631
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
1632
|
+
const requestPath = path === '/' ? '' : path;
|
|
1633
|
+
// Report initial progress if callback provided
|
|
1634
|
+
if (options?.onProgress) {
|
|
1635
|
+
await options.onProgress({
|
|
1636
|
+
progress: 0,
|
|
1637
|
+
total: 100,
|
|
1638
|
+
message: `Starting append_block_children request...`
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
1642
|
+
const requestConfig = {};
|
|
1643
|
+
if (options?.signal) {
|
|
1644
|
+
requestConfig.signal = options.signal;
|
|
1645
|
+
}
|
|
1646
|
+
const response = await this.httpClient.patch(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
1647
|
+
// Report completion progress if callback provided
|
|
1648
|
+
if (options?.onProgress) {
|
|
1649
|
+
await options.onProgress({
|
|
1650
|
+
progress: 100,
|
|
1651
|
+
total: 100,
|
|
1652
|
+
message: `Completed append_block_children request`
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
const duration = Date.now() - startTime;
|
|
1656
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
1657
|
+
endpoint: 'append_block_children',
|
|
1658
|
+
method: 'PATCH',
|
|
1659
|
+
path: '/blocks/{block_id}/children',
|
|
1660
|
+
duration_ms: duration,
|
|
1661
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
1662
|
+
});
|
|
1663
|
+
return {
|
|
1664
|
+
content: [
|
|
1665
|
+
{
|
|
1666
|
+
type: 'text',
|
|
1667
|
+
text: JSON.stringify(response.data, null, 2)
|
|
1668
|
+
}
|
|
1669
|
+
]
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
catch (error) {
|
|
1673
|
+
const duration = Date.now() - startTime;
|
|
1674
|
+
// Check if error is due to cancellation
|
|
1675
|
+
if (axios.isCancel(error)) {
|
|
1676
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
1677
|
+
endpoint: 'append_block_children',
|
|
1678
|
+
method: 'PATCH',
|
|
1679
|
+
path: '/blocks/{block_id}/children',
|
|
1680
|
+
duration_ms: duration
|
|
1681
|
+
});
|
|
1682
|
+
throw new Error('Request was cancelled');
|
|
1683
|
+
}
|
|
1684
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
1685
|
+
endpoint: 'append_block_children',
|
|
1686
|
+
method: 'PATCH',
|
|
1687
|
+
path: '/blocks/{block_id}/children',
|
|
1688
|
+
duration_ms: duration,
|
|
1689
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1690
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
1691
|
+
});
|
|
1692
|
+
throw new Error(`Failed to execute append_block_children: ${error instanceof Error ? error.message : String(error)}`);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
/* DEBUG: endpoint={"name":"get_block","method":"GET","path":"/blocks/{block_id}","description":"Get block by ID","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to fetch","location":"path"}},"response_format":"json","category":"Block Content"} */
|
|
1696
|
+
async getBlock(params, options) {
|
|
1697
|
+
const startTime = Date.now();
|
|
1698
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
1699
|
+
endpoint: 'get_block',
|
|
1700
|
+
method: 'GET',
|
|
1701
|
+
path: '/blocks/{block_id}',
|
|
1702
|
+
paramCount: Object.keys(params || {}).length,
|
|
1703
|
+
paramKeys: Object.keys(params || {})
|
|
1704
|
+
});
|
|
1705
|
+
try {
|
|
1706
|
+
// Extract and separate parameters by location: path, query, body
|
|
1707
|
+
const pathTemplate = '/blocks/{block_id}';
|
|
1708
|
+
const pathParams = {};
|
|
1709
|
+
const queryParams = {};
|
|
1710
|
+
const bodyParams = {};
|
|
1711
|
+
const extractedParams = [];
|
|
1712
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
1713
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
1714
|
+
let match;
|
|
1715
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
1716
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
1717
|
+
if (paramName && params[paramName] !== undefined) {
|
|
1718
|
+
pathParams[paramName] = params[paramName];
|
|
1719
|
+
extractedParams.push(paramName);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
// Handle standard path templates: {resourceName}
|
|
1723
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
1724
|
+
standardPathParams.forEach(paramTemplate => {
|
|
1725
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
1726
|
+
// Only process if not already handled by Google template logic
|
|
1727
|
+
if (!extractedParams.includes(paramName)) {
|
|
1728
|
+
if (params[paramName] !== undefined) {
|
|
1729
|
+
pathParams[paramName] = params[paramName];
|
|
1730
|
+
extractedParams.push(paramName);
|
|
1731
|
+
}
|
|
1732
|
+
else {
|
|
1733
|
+
// Provide default values for optional path parameters
|
|
1734
|
+
if (paramName === 'userId') {
|
|
1735
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
1736
|
+
extractedParams.push(paramName);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
// Check if any parameter has raw_array flag
|
|
1742
|
+
let hasRawArrayBody = false;
|
|
1743
|
+
let rawBodyData = undefined;
|
|
1744
|
+
// Separate remaining parameters by location (query vs body)
|
|
1745
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
1746
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1747
|
+
if (!extractedParams.includes(key)) {
|
|
1748
|
+
bodyParams[key] = value;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
// Validate required parameters
|
|
1752
|
+
const path = this.buildPath('/blocks/{block_id}', pathParams);
|
|
1753
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
1754
|
+
const requestPath = path === '/' ? '' : path;
|
|
1755
|
+
// Report initial progress if callback provided
|
|
1756
|
+
if (options?.onProgress) {
|
|
1757
|
+
await options.onProgress({
|
|
1758
|
+
progress: 0,
|
|
1759
|
+
total: 100,
|
|
1760
|
+
message: `Starting get_block request...`
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
1764
|
+
const requestConfig = {};
|
|
1765
|
+
if (options?.signal) {
|
|
1766
|
+
requestConfig.signal = options.signal;
|
|
1767
|
+
}
|
|
1768
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
1769
|
+
// Report completion progress if callback provided
|
|
1770
|
+
if (options?.onProgress) {
|
|
1771
|
+
await options.onProgress({
|
|
1772
|
+
progress: 100,
|
|
1773
|
+
total: 100,
|
|
1774
|
+
message: `Completed get_block request`
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
const duration = Date.now() - startTime;
|
|
1778
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
1779
|
+
endpoint: 'get_block',
|
|
1780
|
+
method: 'GET',
|
|
1781
|
+
path: '/blocks/{block_id}',
|
|
1782
|
+
duration_ms: duration,
|
|
1783
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
1784
|
+
});
|
|
1785
|
+
return {
|
|
1786
|
+
content: [
|
|
1787
|
+
{
|
|
1788
|
+
type: 'text',
|
|
1789
|
+
text: JSON.stringify(response.data, null, 2)
|
|
1790
|
+
}
|
|
1791
|
+
]
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
catch (error) {
|
|
1795
|
+
const duration = Date.now() - startTime;
|
|
1796
|
+
// Check if error is due to cancellation
|
|
1797
|
+
if (axios.isCancel(error)) {
|
|
1798
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
1799
|
+
endpoint: 'get_block',
|
|
1800
|
+
method: 'GET',
|
|
1801
|
+
path: '/blocks/{block_id}',
|
|
1802
|
+
duration_ms: duration
|
|
1803
|
+
});
|
|
1804
|
+
throw new Error('Request was cancelled');
|
|
1805
|
+
}
|
|
1806
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
1807
|
+
endpoint: 'get_block',
|
|
1808
|
+
method: 'GET',
|
|
1809
|
+
path: '/blocks/{block_id}',
|
|
1810
|
+
duration_ms: duration,
|
|
1811
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1812
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
1813
|
+
});
|
|
1814
|
+
throw new Error(`Failed to execute get_block: ${error instanceof Error ? error.message : String(error)}`);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
/* DEBUG: endpoint={"name":"update_block","method":"PATCH","path":"/blocks/{block_id}","description":"Update block content","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to update","location":"path"},"paragraph":{"type":"object","required":false,"description":"Updated paragraph block content","location":"body"},"heading_1":{"type":"object","required":false,"description":"Updated heading 1 block content","location":"body"},"heading_2":{"type":"object","required":false,"description":"Updated heading 2 block content","location":"body"},"heading_3":{"type":"object","required":false,"description":"Updated heading 3 block content","location":"body"},"bulleted_list_item":{"type":"object","required":false,"description":"Updated bulleted list item content","location":"body"},"numbered_list_item":{"type":"object","required":false,"description":"Updated numbered list item content","location":"body"},"to_do":{"type":"object","required":false,"description":"Updated to-do block content","location":"body"},"toggle":{"type":"object","required":false,"description":"Updated toggle block content","location":"body"},"code":{"type":"object","required":false,"description":"Updated code block content","location":"body"},"embed":{"type":"object","required":false,"description":"Updated embed block content","location":"body"},"image":{"type":"object","required":false,"description":"Updated image block content","location":"body"},"video":{"type":"object","required":false,"description":"Updated video block content","location":"body"},"file":{"type":"object","required":false,"description":"Updated file block content","location":"body"},"pdf":{"type":"object","required":false,"description":"Updated PDF block content","location":"body"},"bookmark":{"type":"object","required":false,"description":"Updated bookmark block content","location":"body"},"callout":{"type":"object","required":false,"description":"Updated callout block content","location":"body"},"quote":{"type":"object","required":false,"description":"Updated quote block content","location":"body"},"equation":{"type":"object","required":false,"description":"Updated equation block content","location":"body"},"divider":{"type":"object","required":false,"description":"Updated divider block content","location":"body"},"table_of_contents":{"type":"object","required":false,"description":"Updated table of contents block content","location":"body"},"column":{"type":"object","required":false,"description":"Updated column block content","location":"body"},"column_list":{"type":"object","required":false,"description":"Updated column list block content","location":"body"},"link_preview":{"type":"object","required":false,"description":"Updated link preview block content","location":"body"},"synced_block":{"type":"object","required":false,"description":"Updated synced block content","location":"body"},"table":{"type":"object","required":false,"description":"Updated table block content","location":"body"},"table_row":{"type":"object","required":false,"description":"Updated table row block content","location":"body"},"archived":{"type":"boolean","required":false,"description":"Archive or unarchive the block","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
1818
|
+
async updateBlock(params, options) {
|
|
1819
|
+
const startTime = Date.now();
|
|
1820
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
1821
|
+
endpoint: 'update_block',
|
|
1822
|
+
method: 'PATCH',
|
|
1823
|
+
path: '/blocks/{block_id}',
|
|
1824
|
+
paramCount: Object.keys(params || {}).length,
|
|
1825
|
+
paramKeys: Object.keys(params || {})
|
|
1826
|
+
});
|
|
1827
|
+
try {
|
|
1828
|
+
// Extract and separate parameters by location: path, query, body
|
|
1829
|
+
const pathTemplate = '/blocks/{block_id}';
|
|
1830
|
+
const pathParams = {};
|
|
1831
|
+
const queryParams = {};
|
|
1832
|
+
const bodyParams = {};
|
|
1833
|
+
const extractedParams = [];
|
|
1834
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
1835
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
1836
|
+
let match;
|
|
1837
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
1838
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
1839
|
+
if (paramName && params[paramName] !== undefined) {
|
|
1840
|
+
pathParams[paramName] = params[paramName];
|
|
1841
|
+
extractedParams.push(paramName);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
// Handle standard path templates: {resourceName}
|
|
1845
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
1846
|
+
standardPathParams.forEach(paramTemplate => {
|
|
1847
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
1848
|
+
// Only process if not already handled by Google template logic
|
|
1849
|
+
if (!extractedParams.includes(paramName)) {
|
|
1850
|
+
if (params[paramName] !== undefined) {
|
|
1851
|
+
pathParams[paramName] = params[paramName];
|
|
1852
|
+
extractedParams.push(paramName);
|
|
1853
|
+
}
|
|
1854
|
+
else {
|
|
1855
|
+
// Provide default values for optional path parameters
|
|
1856
|
+
if (paramName === 'userId') {
|
|
1857
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
1858
|
+
extractedParams.push(paramName);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
// Check if any parameter has raw_array flag
|
|
1864
|
+
let hasRawArrayBody = false;
|
|
1865
|
+
let rawBodyData = undefined;
|
|
1866
|
+
// Separate remaining parameters by location (query vs body)
|
|
1867
|
+
if (params[""] !== undefined) {
|
|
1868
|
+
bodyParams[""] = params[""];
|
|
1869
|
+
extractedParams.push("");
|
|
1870
|
+
}
|
|
1871
|
+
if (params[""] !== undefined) {
|
|
1872
|
+
bodyParams[""] = params[""];
|
|
1873
|
+
extractedParams.push("");
|
|
1874
|
+
}
|
|
1875
|
+
if (params[""] !== undefined) {
|
|
1876
|
+
bodyParams[""] = params[""];
|
|
1877
|
+
extractedParams.push("");
|
|
1878
|
+
}
|
|
1879
|
+
if (params[""] !== undefined) {
|
|
1880
|
+
bodyParams[""] = params[""];
|
|
1881
|
+
extractedParams.push("");
|
|
1882
|
+
}
|
|
1883
|
+
if (params[""] !== undefined) {
|
|
1884
|
+
bodyParams[""] = params[""];
|
|
1885
|
+
extractedParams.push("");
|
|
1886
|
+
}
|
|
1887
|
+
if (params[""] !== undefined) {
|
|
1888
|
+
bodyParams[""] = params[""];
|
|
1889
|
+
extractedParams.push("");
|
|
1890
|
+
}
|
|
1891
|
+
if (params[""] !== undefined) {
|
|
1892
|
+
bodyParams[""] = params[""];
|
|
1893
|
+
extractedParams.push("");
|
|
1894
|
+
}
|
|
1895
|
+
if (params[""] !== undefined) {
|
|
1896
|
+
bodyParams[""] = params[""];
|
|
1897
|
+
extractedParams.push("");
|
|
1898
|
+
}
|
|
1899
|
+
if (params[""] !== undefined) {
|
|
1900
|
+
bodyParams[""] = params[""];
|
|
1901
|
+
extractedParams.push("");
|
|
1902
|
+
}
|
|
1903
|
+
if (params[""] !== undefined) {
|
|
1904
|
+
bodyParams[""] = params[""];
|
|
1905
|
+
extractedParams.push("");
|
|
1906
|
+
}
|
|
1907
|
+
if (params[""] !== undefined) {
|
|
1908
|
+
bodyParams[""] = params[""];
|
|
1909
|
+
extractedParams.push("");
|
|
1910
|
+
}
|
|
1911
|
+
if (params[""] !== undefined) {
|
|
1912
|
+
bodyParams[""] = params[""];
|
|
1913
|
+
extractedParams.push("");
|
|
1914
|
+
}
|
|
1915
|
+
if (params[""] !== undefined) {
|
|
1916
|
+
bodyParams[""] = params[""];
|
|
1917
|
+
extractedParams.push("");
|
|
1918
|
+
}
|
|
1919
|
+
if (params[""] !== undefined) {
|
|
1920
|
+
bodyParams[""] = params[""];
|
|
1921
|
+
extractedParams.push("");
|
|
1922
|
+
}
|
|
1923
|
+
if (params[""] !== undefined) {
|
|
1924
|
+
bodyParams[""] = params[""];
|
|
1925
|
+
extractedParams.push("");
|
|
1926
|
+
}
|
|
1927
|
+
if (params[""] !== undefined) {
|
|
1928
|
+
bodyParams[""] = params[""];
|
|
1929
|
+
extractedParams.push("");
|
|
1930
|
+
}
|
|
1931
|
+
if (params[""] !== undefined) {
|
|
1932
|
+
bodyParams[""] = params[""];
|
|
1933
|
+
extractedParams.push("");
|
|
1934
|
+
}
|
|
1935
|
+
if (params[""] !== undefined) {
|
|
1936
|
+
bodyParams[""] = params[""];
|
|
1937
|
+
extractedParams.push("");
|
|
1938
|
+
}
|
|
1939
|
+
if (params[""] !== undefined) {
|
|
1940
|
+
bodyParams[""] = params[""];
|
|
1941
|
+
extractedParams.push("");
|
|
1942
|
+
}
|
|
1943
|
+
if (params[""] !== undefined) {
|
|
1944
|
+
bodyParams[""] = params[""];
|
|
1945
|
+
extractedParams.push("");
|
|
1946
|
+
}
|
|
1947
|
+
if (params[""] !== undefined) {
|
|
1948
|
+
bodyParams[""] = params[""];
|
|
1949
|
+
extractedParams.push("");
|
|
1950
|
+
}
|
|
1951
|
+
if (params[""] !== undefined) {
|
|
1952
|
+
bodyParams[""] = params[""];
|
|
1953
|
+
extractedParams.push("");
|
|
1954
|
+
}
|
|
1955
|
+
if (params[""] !== undefined) {
|
|
1956
|
+
bodyParams[""] = params[""];
|
|
1957
|
+
extractedParams.push("");
|
|
1958
|
+
}
|
|
1959
|
+
if (params[""] !== undefined) {
|
|
1960
|
+
bodyParams[""] = params[""];
|
|
1961
|
+
extractedParams.push("");
|
|
1962
|
+
}
|
|
1963
|
+
if (params[""] !== undefined) {
|
|
1964
|
+
bodyParams[""] = params[""];
|
|
1965
|
+
extractedParams.push("");
|
|
1966
|
+
}
|
|
1967
|
+
if (params[""] !== undefined) {
|
|
1968
|
+
bodyParams[""] = params[""];
|
|
1969
|
+
extractedParams.push("");
|
|
1970
|
+
}
|
|
1971
|
+
if (params[""] !== undefined) {
|
|
1972
|
+
bodyParams[""] = params[""];
|
|
1973
|
+
extractedParams.push("");
|
|
1974
|
+
}
|
|
1975
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
1976
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1977
|
+
if (!extractedParams.includes(key)) {
|
|
1978
|
+
bodyParams[key] = value;
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
// Validate required parameters
|
|
1982
|
+
const path = this.buildPath('/blocks/{block_id}', pathParams);
|
|
1983
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
1984
|
+
const requestPath = path === '/' ? '' : path;
|
|
1985
|
+
// Report initial progress if callback provided
|
|
1986
|
+
if (options?.onProgress) {
|
|
1987
|
+
await options.onProgress({
|
|
1988
|
+
progress: 0,
|
|
1989
|
+
total: 100,
|
|
1990
|
+
message: `Starting update_block request...`
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
1994
|
+
const requestConfig = {};
|
|
1995
|
+
if (options?.signal) {
|
|
1996
|
+
requestConfig.signal = options.signal;
|
|
1997
|
+
}
|
|
1998
|
+
const response = await this.httpClient.patch(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
1999
|
+
// Report completion progress if callback provided
|
|
2000
|
+
if (options?.onProgress) {
|
|
2001
|
+
await options.onProgress({
|
|
2002
|
+
progress: 100,
|
|
2003
|
+
total: 100,
|
|
2004
|
+
message: `Completed update_block request`
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
const duration = Date.now() - startTime;
|
|
2008
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
2009
|
+
endpoint: 'update_block',
|
|
2010
|
+
method: 'PATCH',
|
|
2011
|
+
path: '/blocks/{block_id}',
|
|
2012
|
+
duration_ms: duration,
|
|
2013
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
2014
|
+
});
|
|
2015
|
+
return {
|
|
2016
|
+
content: [
|
|
2017
|
+
{
|
|
2018
|
+
type: 'text',
|
|
2019
|
+
text: JSON.stringify(response.data, null, 2)
|
|
2020
|
+
}
|
|
2021
|
+
]
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
catch (error) {
|
|
2025
|
+
const duration = Date.now() - startTime;
|
|
2026
|
+
// Check if error is due to cancellation
|
|
2027
|
+
if (axios.isCancel(error)) {
|
|
2028
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
2029
|
+
endpoint: 'update_block',
|
|
2030
|
+
method: 'PATCH',
|
|
2031
|
+
path: '/blocks/{block_id}',
|
|
2032
|
+
duration_ms: duration
|
|
2033
|
+
});
|
|
2034
|
+
throw new Error('Request was cancelled');
|
|
2035
|
+
}
|
|
2036
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
2037
|
+
endpoint: 'update_block',
|
|
2038
|
+
method: 'PATCH',
|
|
2039
|
+
path: '/blocks/{block_id}',
|
|
2040
|
+
duration_ms: duration,
|
|
2041
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2042
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
2043
|
+
});
|
|
2044
|
+
throw new Error(`Failed to execute update_block: ${error instanceof Error ? error.message : String(error)}`);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
/* DEBUG: endpoint={"name":"delete_block","method":"DELETE","path":"/blocks/{block_id}","description":"Delete a block","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to delete","location":"path"}},"response_format":"json","category":"Block Content"} */
|
|
2048
|
+
async deleteBlock(params, options) {
|
|
2049
|
+
const startTime = Date.now();
|
|
2050
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2051
|
+
endpoint: 'delete_block',
|
|
2052
|
+
method: 'DELETE',
|
|
2053
|
+
path: '/blocks/{block_id}',
|
|
2054
|
+
paramCount: Object.keys(params || {}).length,
|
|
2055
|
+
paramKeys: Object.keys(params || {})
|
|
2056
|
+
});
|
|
2057
|
+
try {
|
|
2058
|
+
// Extract and separate parameters by location: path, query, body
|
|
2059
|
+
const pathTemplate = '/blocks/{block_id}';
|
|
2060
|
+
const pathParams = {};
|
|
2061
|
+
const queryParams = {};
|
|
2062
|
+
const bodyParams = {};
|
|
2063
|
+
const extractedParams = [];
|
|
2064
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
2065
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
2066
|
+
let match;
|
|
2067
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
2068
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
2069
|
+
if (paramName && params[paramName] !== undefined) {
|
|
2070
|
+
pathParams[paramName] = params[paramName];
|
|
2071
|
+
extractedParams.push(paramName);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
// Handle standard path templates: {resourceName}
|
|
2075
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
2076
|
+
standardPathParams.forEach(paramTemplate => {
|
|
2077
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
2078
|
+
// Only process if not already handled by Google template logic
|
|
2079
|
+
if (!extractedParams.includes(paramName)) {
|
|
2080
|
+
if (params[paramName] !== undefined) {
|
|
2081
|
+
pathParams[paramName] = params[paramName];
|
|
2082
|
+
extractedParams.push(paramName);
|
|
2083
|
+
}
|
|
2084
|
+
else {
|
|
2085
|
+
// Provide default values for optional path parameters
|
|
2086
|
+
if (paramName === 'userId') {
|
|
2087
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
2088
|
+
extractedParams.push(paramName);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
});
|
|
2093
|
+
// Check if any parameter has raw_array flag
|
|
2094
|
+
let hasRawArrayBody = false;
|
|
2095
|
+
let rawBodyData = undefined;
|
|
2096
|
+
// Separate remaining parameters by location (query vs body)
|
|
2097
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
2098
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2099
|
+
if (!extractedParams.includes(key)) {
|
|
2100
|
+
bodyParams[key] = value;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
// Validate required parameters
|
|
2104
|
+
const path = this.buildPath('/blocks/{block_id}', pathParams);
|
|
2105
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
2106
|
+
const requestPath = path === '/' ? '' : path;
|
|
2107
|
+
// Report initial progress if callback provided
|
|
2108
|
+
if (options?.onProgress) {
|
|
2109
|
+
await options.onProgress({
|
|
2110
|
+
progress: 0,
|
|
2111
|
+
total: 100,
|
|
2112
|
+
message: `Starting delete_block request...`
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
2116
|
+
const requestConfig = {};
|
|
2117
|
+
if (options?.signal) {
|
|
2118
|
+
requestConfig.signal = options.signal;
|
|
2119
|
+
}
|
|
2120
|
+
const response = await this.httpClient.delete(requestPath, { params: queryParams, ...requestConfig });
|
|
2121
|
+
// Report completion progress if callback provided
|
|
2122
|
+
if (options?.onProgress) {
|
|
2123
|
+
await options.onProgress({
|
|
2124
|
+
progress: 100,
|
|
2125
|
+
total: 100,
|
|
2126
|
+
message: `Completed delete_block request`
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
const duration = Date.now() - startTime;
|
|
2130
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
2131
|
+
endpoint: 'delete_block',
|
|
2132
|
+
method: 'DELETE',
|
|
2133
|
+
path: '/blocks/{block_id}',
|
|
2134
|
+
duration_ms: duration,
|
|
2135
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
2136
|
+
});
|
|
2137
|
+
return {
|
|
2138
|
+
content: [
|
|
2139
|
+
{
|
|
2140
|
+
type: 'text',
|
|
2141
|
+
text: JSON.stringify(response.data, null, 2)
|
|
2142
|
+
}
|
|
2143
|
+
]
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
catch (error) {
|
|
2147
|
+
const duration = Date.now() - startTime;
|
|
2148
|
+
// Check if error is due to cancellation
|
|
2149
|
+
if (axios.isCancel(error)) {
|
|
2150
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
2151
|
+
endpoint: 'delete_block',
|
|
2152
|
+
method: 'DELETE',
|
|
2153
|
+
path: '/blocks/{block_id}',
|
|
2154
|
+
duration_ms: duration
|
|
2155
|
+
});
|
|
2156
|
+
throw new Error('Request was cancelled');
|
|
2157
|
+
}
|
|
2158
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
2159
|
+
endpoint: 'delete_block',
|
|
2160
|
+
method: 'DELETE',
|
|
2161
|
+
path: '/blocks/{block_id}',
|
|
2162
|
+
duration_ms: duration,
|
|
2163
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2164
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
2165
|
+
});
|
|
2166
|
+
throw new Error(`Failed to execute delete_block: ${error instanceof Error ? error.message : String(error)}`);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
/* DEBUG: endpoint={"name":"list_users","method":"GET","path":"/users","description":"List all users","parameters":{"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"query"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"query","default":100}},"response_format":"json","category":"Workspace Collaboration"} */
|
|
2170
|
+
async listUsers(params, options) {
|
|
2171
|
+
const startTime = Date.now();
|
|
2172
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2173
|
+
endpoint: 'list_users',
|
|
2174
|
+
method: 'GET',
|
|
2175
|
+
path: '/users',
|
|
2176
|
+
paramCount: Object.keys(params || {}).length,
|
|
2177
|
+
paramKeys: Object.keys(params || {})
|
|
2178
|
+
});
|
|
2179
|
+
try {
|
|
2180
|
+
// Extract and separate parameters by location: path, query, body
|
|
2181
|
+
const pathTemplate = '/users';
|
|
2182
|
+
const pathParams = {};
|
|
2183
|
+
const queryParams = {};
|
|
2184
|
+
const bodyParams = {};
|
|
2185
|
+
const extractedParams = [];
|
|
2186
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
2187
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
2188
|
+
let match;
|
|
2189
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
2190
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
2191
|
+
if (paramName && params[paramName] !== undefined) {
|
|
2192
|
+
pathParams[paramName] = params[paramName];
|
|
2193
|
+
extractedParams.push(paramName);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
// Handle standard path templates: {resourceName}
|
|
2197
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
2198
|
+
standardPathParams.forEach(paramTemplate => {
|
|
2199
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
2200
|
+
// Only process if not already handled by Google template logic
|
|
2201
|
+
if (!extractedParams.includes(paramName)) {
|
|
2202
|
+
if (params[paramName] !== undefined) {
|
|
2203
|
+
pathParams[paramName] = params[paramName];
|
|
2204
|
+
extractedParams.push(paramName);
|
|
2205
|
+
}
|
|
2206
|
+
else {
|
|
2207
|
+
// Provide default values for optional path parameters
|
|
2208
|
+
if (paramName === 'userId') {
|
|
2209
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
2210
|
+
extractedParams.push(paramName);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
});
|
|
2215
|
+
// Check if any parameter has raw_array flag
|
|
2216
|
+
let hasRawArrayBody = false;
|
|
2217
|
+
let rawBodyData = undefined;
|
|
2218
|
+
// Separate remaining parameters by location (query vs body)
|
|
2219
|
+
if (params[""] !== undefined) {
|
|
2220
|
+
queryParams[""] = params[""];
|
|
2221
|
+
extractedParams.push("");
|
|
2222
|
+
}
|
|
2223
|
+
if (params[""] !== undefined) {
|
|
2224
|
+
queryParams[""] = params[""];
|
|
2225
|
+
extractedParams.push("");
|
|
2226
|
+
}
|
|
2227
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
2228
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2229
|
+
if (!extractedParams.includes(key)) {
|
|
2230
|
+
bodyParams[key] = value;
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
// Validate required parameters
|
|
2234
|
+
const path = this.buildPath('/users', pathParams);
|
|
2235
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
2236
|
+
const requestPath = path === '/' ? '' : path;
|
|
2237
|
+
// Report initial progress if callback provided
|
|
2238
|
+
if (options?.onProgress) {
|
|
2239
|
+
await options.onProgress({
|
|
2240
|
+
progress: 0,
|
|
2241
|
+
total: 100,
|
|
2242
|
+
message: `Starting list_users request...`
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
2246
|
+
const requestConfig = {};
|
|
2247
|
+
if (options?.signal) {
|
|
2248
|
+
requestConfig.signal = options.signal;
|
|
2249
|
+
}
|
|
2250
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
2251
|
+
// Report completion progress if callback provided
|
|
2252
|
+
if (options?.onProgress) {
|
|
2253
|
+
await options.onProgress({
|
|
2254
|
+
progress: 100,
|
|
2255
|
+
total: 100,
|
|
2256
|
+
message: `Completed list_users request`
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
const duration = Date.now() - startTime;
|
|
2260
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
2261
|
+
endpoint: 'list_users',
|
|
2262
|
+
method: 'GET',
|
|
2263
|
+
path: '/users',
|
|
2264
|
+
duration_ms: duration,
|
|
2265
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
2266
|
+
});
|
|
2267
|
+
return {
|
|
2268
|
+
content: [
|
|
2269
|
+
{
|
|
2270
|
+
type: 'text',
|
|
2271
|
+
text: JSON.stringify(response.data, null, 2)
|
|
2272
|
+
}
|
|
2273
|
+
]
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
catch (error) {
|
|
2277
|
+
const duration = Date.now() - startTime;
|
|
2278
|
+
// Check if error is due to cancellation
|
|
2279
|
+
if (axios.isCancel(error)) {
|
|
2280
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
2281
|
+
endpoint: 'list_users',
|
|
2282
|
+
method: 'GET',
|
|
2283
|
+
path: '/users',
|
|
2284
|
+
duration_ms: duration
|
|
2285
|
+
});
|
|
2286
|
+
throw new Error('Request was cancelled');
|
|
2287
|
+
}
|
|
2288
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
2289
|
+
endpoint: 'list_users',
|
|
2290
|
+
method: 'GET',
|
|
2291
|
+
path: '/users',
|
|
2292
|
+
duration_ms: duration,
|
|
2293
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2294
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
2295
|
+
});
|
|
2296
|
+
throw new Error(`Failed to execute list_users: ${error instanceof Error ? error.message : String(error)}`);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
/* DEBUG: endpoint={"name":"get_user","method":"GET","path":"/users/{user_id}","description":"Get user by ID","parameters":{"user_id":{"type":"string","required":true,"description":"User ID to fetch","location":"path"}},"response_format":"json","category":"Workspace Collaboration"} */
|
|
2300
|
+
async getUser(params, options) {
|
|
2301
|
+
const startTime = Date.now();
|
|
2302
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2303
|
+
endpoint: 'get_user',
|
|
2304
|
+
method: 'GET',
|
|
2305
|
+
path: '/users/{user_id}',
|
|
2306
|
+
paramCount: Object.keys(params || {}).length,
|
|
2307
|
+
paramKeys: Object.keys(params || {})
|
|
2308
|
+
});
|
|
2309
|
+
try {
|
|
2310
|
+
// Extract and separate parameters by location: path, query, body
|
|
2311
|
+
const pathTemplate = '/users/{user_id}';
|
|
2312
|
+
const pathParams = {};
|
|
2313
|
+
const queryParams = {};
|
|
2314
|
+
const bodyParams = {};
|
|
2315
|
+
const extractedParams = [];
|
|
2316
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
2317
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
2318
|
+
let match;
|
|
2319
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
2320
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
2321
|
+
if (paramName && params[paramName] !== undefined) {
|
|
2322
|
+
pathParams[paramName] = params[paramName];
|
|
2323
|
+
extractedParams.push(paramName);
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
// Handle standard path templates: {resourceName}
|
|
2327
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
2328
|
+
standardPathParams.forEach(paramTemplate => {
|
|
2329
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
2330
|
+
// Only process if not already handled by Google template logic
|
|
2331
|
+
if (!extractedParams.includes(paramName)) {
|
|
2332
|
+
if (params[paramName] !== undefined) {
|
|
2333
|
+
pathParams[paramName] = params[paramName];
|
|
2334
|
+
extractedParams.push(paramName);
|
|
2335
|
+
}
|
|
2336
|
+
else {
|
|
2337
|
+
// Provide default values for optional path parameters
|
|
2338
|
+
if (paramName === 'userId') {
|
|
2339
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
2340
|
+
extractedParams.push(paramName);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
});
|
|
2345
|
+
// Check if any parameter has raw_array flag
|
|
2346
|
+
let hasRawArrayBody = false;
|
|
2347
|
+
let rawBodyData = undefined;
|
|
2348
|
+
// Separate remaining parameters by location (query vs body)
|
|
2349
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
2350
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2351
|
+
if (!extractedParams.includes(key)) {
|
|
2352
|
+
bodyParams[key] = value;
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
// Validate required parameters
|
|
2356
|
+
const path = this.buildPath('/users/{user_id}', pathParams);
|
|
2357
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
2358
|
+
const requestPath = path === '/' ? '' : path;
|
|
2359
|
+
// Report initial progress if callback provided
|
|
2360
|
+
if (options?.onProgress) {
|
|
2361
|
+
await options.onProgress({
|
|
2362
|
+
progress: 0,
|
|
2363
|
+
total: 100,
|
|
2364
|
+
message: `Starting get_user request...`
|
|
2365
|
+
});
|
|
2366
|
+
}
|
|
2367
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
2368
|
+
const requestConfig = {};
|
|
2369
|
+
if (options?.signal) {
|
|
2370
|
+
requestConfig.signal = options.signal;
|
|
2371
|
+
}
|
|
2372
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
2373
|
+
// Report completion progress if callback provided
|
|
2374
|
+
if (options?.onProgress) {
|
|
2375
|
+
await options.onProgress({
|
|
2376
|
+
progress: 100,
|
|
2377
|
+
total: 100,
|
|
2378
|
+
message: `Completed get_user request`
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2381
|
+
const duration = Date.now() - startTime;
|
|
2382
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
2383
|
+
endpoint: 'get_user',
|
|
2384
|
+
method: 'GET',
|
|
2385
|
+
path: '/users/{user_id}',
|
|
2386
|
+
duration_ms: duration,
|
|
2387
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
2388
|
+
});
|
|
2389
|
+
return {
|
|
2390
|
+
content: [
|
|
2391
|
+
{
|
|
2392
|
+
type: 'text',
|
|
2393
|
+
text: JSON.stringify(response.data, null, 2)
|
|
2394
|
+
}
|
|
2395
|
+
]
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
catch (error) {
|
|
2399
|
+
const duration = Date.now() - startTime;
|
|
2400
|
+
// Check if error is due to cancellation
|
|
2401
|
+
if (axios.isCancel(error)) {
|
|
2402
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
2403
|
+
endpoint: 'get_user',
|
|
2404
|
+
method: 'GET',
|
|
2405
|
+
path: '/users/{user_id}',
|
|
2406
|
+
duration_ms: duration
|
|
2407
|
+
});
|
|
2408
|
+
throw new Error('Request was cancelled');
|
|
2409
|
+
}
|
|
2410
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
2411
|
+
endpoint: 'get_user',
|
|
2412
|
+
method: 'GET',
|
|
2413
|
+
path: '/users/{user_id}',
|
|
2414
|
+
duration_ms: duration,
|
|
2415
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2416
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
2417
|
+
});
|
|
2418
|
+
throw new Error(`Failed to execute get_user: ${error instanceof Error ? error.message : String(error)}`);
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
/* DEBUG: endpoint={"name":"get_me","method":"GET","path":"/users/me","description":"Get current bot user","parameters":{},"response_format":"json","category":"Workspace Collaboration"} */
|
|
2422
|
+
async getMe(params, options) {
|
|
2423
|
+
const startTime = Date.now();
|
|
2424
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2425
|
+
endpoint: 'get_me',
|
|
2426
|
+
method: 'GET',
|
|
2427
|
+
path: '/users/me',
|
|
2428
|
+
paramCount: Object.keys(params || {}).length,
|
|
2429
|
+
paramKeys: Object.keys(params || {})
|
|
2430
|
+
});
|
|
2431
|
+
try {
|
|
2432
|
+
// Extract and separate parameters by location: path, query, body
|
|
2433
|
+
const pathTemplate = '/users/me';
|
|
2434
|
+
const pathParams = {};
|
|
2435
|
+
const queryParams = {};
|
|
2436
|
+
const bodyParams = {};
|
|
2437
|
+
const extractedParams = [];
|
|
2438
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
2439
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
2440
|
+
let match;
|
|
2441
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
2442
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
2443
|
+
if (paramName && params[paramName] !== undefined) {
|
|
2444
|
+
pathParams[paramName] = params[paramName];
|
|
2445
|
+
extractedParams.push(paramName);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
// Handle standard path templates: {resourceName}
|
|
2449
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
2450
|
+
standardPathParams.forEach(paramTemplate => {
|
|
2451
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
2452
|
+
// Only process if not already handled by Google template logic
|
|
2453
|
+
if (!extractedParams.includes(paramName)) {
|
|
2454
|
+
if (params[paramName] !== undefined) {
|
|
2455
|
+
pathParams[paramName] = params[paramName];
|
|
2456
|
+
extractedParams.push(paramName);
|
|
2457
|
+
}
|
|
2458
|
+
else {
|
|
2459
|
+
// Provide default values for optional path parameters
|
|
2460
|
+
if (paramName === 'userId') {
|
|
2461
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
2462
|
+
extractedParams.push(paramName);
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
});
|
|
2467
|
+
// Check if any parameter has raw_array flag
|
|
2468
|
+
let hasRawArrayBody = false;
|
|
2469
|
+
let rawBodyData = undefined;
|
|
2470
|
+
// Separate remaining parameters by location (query vs body)
|
|
2471
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
2472
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2473
|
+
if (!extractedParams.includes(key)) {
|
|
2474
|
+
bodyParams[key] = value;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
// Validate required parameters
|
|
2478
|
+
const path = this.buildPath('/users/me', pathParams);
|
|
2479
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
2480
|
+
const requestPath = path === '/' ? '' : path;
|
|
2481
|
+
// Report initial progress if callback provided
|
|
2482
|
+
if (options?.onProgress) {
|
|
2483
|
+
await options.onProgress({
|
|
2484
|
+
progress: 0,
|
|
2485
|
+
total: 100,
|
|
2486
|
+
message: `Starting get_me request...`
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
2489
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
2490
|
+
const requestConfig = {};
|
|
2491
|
+
if (options?.signal) {
|
|
2492
|
+
requestConfig.signal = options.signal;
|
|
2493
|
+
}
|
|
2494
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
2495
|
+
// Report completion progress if callback provided
|
|
2496
|
+
if (options?.onProgress) {
|
|
2497
|
+
await options.onProgress({
|
|
2498
|
+
progress: 100,
|
|
2499
|
+
total: 100,
|
|
2500
|
+
message: `Completed get_me request`
|
|
2501
|
+
});
|
|
2502
|
+
}
|
|
2503
|
+
const duration = Date.now() - startTime;
|
|
2504
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
2505
|
+
endpoint: 'get_me',
|
|
2506
|
+
method: 'GET',
|
|
2507
|
+
path: '/users/me',
|
|
2508
|
+
duration_ms: duration,
|
|
2509
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
2510
|
+
});
|
|
2511
|
+
return {
|
|
2512
|
+
content: [
|
|
2513
|
+
{
|
|
2514
|
+
type: 'text',
|
|
2515
|
+
text: JSON.stringify(response.data, null, 2)
|
|
2516
|
+
}
|
|
2517
|
+
]
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
catch (error) {
|
|
2521
|
+
const duration = Date.now() - startTime;
|
|
2522
|
+
// Check if error is due to cancellation
|
|
2523
|
+
if (axios.isCancel(error)) {
|
|
2524
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
2525
|
+
endpoint: 'get_me',
|
|
2526
|
+
method: 'GET',
|
|
2527
|
+
path: '/users/me',
|
|
2528
|
+
duration_ms: duration
|
|
2529
|
+
});
|
|
2530
|
+
throw new Error('Request was cancelled');
|
|
2531
|
+
}
|
|
2532
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
2533
|
+
endpoint: 'get_me',
|
|
2534
|
+
method: 'GET',
|
|
2535
|
+
path: '/users/me',
|
|
2536
|
+
duration_ms: duration,
|
|
2537
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2538
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
2539
|
+
});
|
|
2540
|
+
throw new Error(`Failed to execute get_me: ${error instanceof Error ? error.message : String(error)}`);
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
/* DEBUG: endpoint={"name":"search","method":"POST","path":"/search","description":"Search pages and databases","parameters":{"query":{"type":"string","required":false,"description":"Search query string","location":"body"},"sort":{"type":"object","required":false,"description":"Sort configuration","location":"body"},"filter":{"type":"object","required":false,"description":"Filter configuration","location":"body"},"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"body"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"body","default":100}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
2544
|
+
async search(params, options) {
|
|
2545
|
+
const startTime = Date.now();
|
|
2546
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2547
|
+
endpoint: 'search',
|
|
2548
|
+
method: 'POST',
|
|
2549
|
+
path: '/search',
|
|
2550
|
+
paramCount: Object.keys(params || {}).length,
|
|
2551
|
+
paramKeys: Object.keys(params || {})
|
|
2552
|
+
});
|
|
2553
|
+
try {
|
|
2554
|
+
// Extract and separate parameters by location: path, query, body
|
|
2555
|
+
const pathTemplate = '/search';
|
|
2556
|
+
const pathParams = {};
|
|
2557
|
+
const queryParams = {};
|
|
2558
|
+
const bodyParams = {};
|
|
2559
|
+
const extractedParams = [];
|
|
2560
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
2561
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
2562
|
+
let match;
|
|
2563
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
2564
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
2565
|
+
if (paramName && params[paramName] !== undefined) {
|
|
2566
|
+
pathParams[paramName] = params[paramName];
|
|
2567
|
+
extractedParams.push(paramName);
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
// Handle standard path templates: {resourceName}
|
|
2571
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
2572
|
+
standardPathParams.forEach(paramTemplate => {
|
|
2573
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
2574
|
+
// Only process if not already handled by Google template logic
|
|
2575
|
+
if (!extractedParams.includes(paramName)) {
|
|
2576
|
+
if (params[paramName] !== undefined) {
|
|
2577
|
+
pathParams[paramName] = params[paramName];
|
|
2578
|
+
extractedParams.push(paramName);
|
|
2579
|
+
}
|
|
2580
|
+
else {
|
|
2581
|
+
// Provide default values for optional path parameters
|
|
2582
|
+
if (paramName === 'userId') {
|
|
2583
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
2584
|
+
extractedParams.push(paramName);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
});
|
|
2589
|
+
// Check if any parameter has raw_array flag
|
|
2590
|
+
let hasRawArrayBody = false;
|
|
2591
|
+
let rawBodyData = undefined;
|
|
2592
|
+
// Separate remaining parameters by location (query vs body)
|
|
2593
|
+
if (params[""] !== undefined) {
|
|
2594
|
+
bodyParams[""] = params[""];
|
|
2595
|
+
extractedParams.push("");
|
|
2596
|
+
}
|
|
2597
|
+
if (params[""] !== undefined) {
|
|
2598
|
+
bodyParams[""] = params[""];
|
|
2599
|
+
extractedParams.push("");
|
|
2600
|
+
}
|
|
2601
|
+
if (params[""] !== undefined) {
|
|
2602
|
+
bodyParams[""] = params[""];
|
|
2603
|
+
extractedParams.push("");
|
|
2604
|
+
}
|
|
2605
|
+
if (params[""] !== undefined) {
|
|
2606
|
+
bodyParams[""] = params[""];
|
|
2607
|
+
extractedParams.push("");
|
|
2608
|
+
}
|
|
2609
|
+
if (params[""] !== undefined) {
|
|
2610
|
+
bodyParams[""] = params[""];
|
|
2611
|
+
extractedParams.push("");
|
|
2612
|
+
}
|
|
2613
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
2614
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2615
|
+
if (!extractedParams.includes(key)) {
|
|
2616
|
+
bodyParams[key] = value;
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
// Validate required parameters
|
|
2620
|
+
const path = this.buildPath('/search', pathParams);
|
|
2621
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
2622
|
+
const requestPath = path === '/' ? '' : path;
|
|
2623
|
+
// Report initial progress if callback provided
|
|
2624
|
+
if (options?.onProgress) {
|
|
2625
|
+
await options.onProgress({
|
|
2626
|
+
progress: 0,
|
|
2627
|
+
total: 100,
|
|
2628
|
+
message: `Starting search request...`
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2631
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
2632
|
+
const requestConfig = {};
|
|
2633
|
+
if (options?.signal) {
|
|
2634
|
+
requestConfig.signal = options.signal;
|
|
2635
|
+
}
|
|
2636
|
+
const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
2637
|
+
// Report completion progress if callback provided
|
|
2638
|
+
if (options?.onProgress) {
|
|
2639
|
+
await options.onProgress({
|
|
2640
|
+
progress: 100,
|
|
2641
|
+
total: 100,
|
|
2642
|
+
message: `Completed search request`
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
const duration = Date.now() - startTime;
|
|
2646
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
2647
|
+
endpoint: 'search',
|
|
2648
|
+
method: 'POST',
|
|
2649
|
+
path: '/search',
|
|
2650
|
+
duration_ms: duration,
|
|
2651
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
2652
|
+
});
|
|
2653
|
+
return {
|
|
2654
|
+
content: [
|
|
2655
|
+
{
|
|
2656
|
+
type: 'text',
|
|
2657
|
+
text: JSON.stringify(response.data, null, 2)
|
|
2658
|
+
}
|
|
2659
|
+
]
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
catch (error) {
|
|
2663
|
+
const duration = Date.now() - startTime;
|
|
2664
|
+
// Check if error is due to cancellation
|
|
2665
|
+
if (axios.isCancel(error)) {
|
|
2666
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
2667
|
+
endpoint: 'search',
|
|
2668
|
+
method: 'POST',
|
|
2669
|
+
path: '/search',
|
|
2670
|
+
duration_ms: duration
|
|
2671
|
+
});
|
|
2672
|
+
throw new Error('Request was cancelled');
|
|
2673
|
+
}
|
|
2674
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
2675
|
+
endpoint: 'search',
|
|
2676
|
+
method: 'POST',
|
|
2677
|
+
path: '/search',
|
|
2678
|
+
duration_ms: duration,
|
|
2679
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2680
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
2681
|
+
});
|
|
2682
|
+
throw new Error(`Failed to execute search: ${error instanceof Error ? error.message : String(error)}`);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
/* DEBUG: endpoint={"name":"create_comment","method":"POST","path":"/comments","description":"Create a comment on a page or block","parameters":{"parent":{"type":"object","required":true,"description":"Parent object (page or block)","location":"body"},"rich_text":{"type":"array","required":true,"description":"Array of rich text objects for comment content","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
2686
|
+
async createComment(params, options) {
|
|
2687
|
+
const startTime = Date.now();
|
|
2688
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2689
|
+
endpoint: 'create_comment',
|
|
2690
|
+
method: 'POST',
|
|
2691
|
+
path: '/comments',
|
|
2692
|
+
paramCount: Object.keys(params || {}).length,
|
|
2693
|
+
paramKeys: Object.keys(params || {})
|
|
2694
|
+
});
|
|
2695
|
+
try {
|
|
2696
|
+
// Extract and separate parameters by location: path, query, body
|
|
2697
|
+
const pathTemplate = '/comments';
|
|
2698
|
+
const pathParams = {};
|
|
2699
|
+
const queryParams = {};
|
|
2700
|
+
const bodyParams = {};
|
|
2701
|
+
const extractedParams = [];
|
|
2702
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
2703
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
2704
|
+
let match;
|
|
2705
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
2706
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
2707
|
+
if (paramName && params[paramName] !== undefined) {
|
|
2708
|
+
pathParams[paramName] = params[paramName];
|
|
2709
|
+
extractedParams.push(paramName);
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
// Handle standard path templates: {resourceName}
|
|
2713
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
2714
|
+
standardPathParams.forEach(paramTemplate => {
|
|
2715
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
2716
|
+
// Only process if not already handled by Google template logic
|
|
2717
|
+
if (!extractedParams.includes(paramName)) {
|
|
2718
|
+
if (params[paramName] !== undefined) {
|
|
2719
|
+
pathParams[paramName] = params[paramName];
|
|
2720
|
+
extractedParams.push(paramName);
|
|
2721
|
+
}
|
|
2722
|
+
else {
|
|
2723
|
+
// Provide default values for optional path parameters
|
|
2724
|
+
if (paramName === 'userId') {
|
|
2725
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
2726
|
+
extractedParams.push(paramName);
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
});
|
|
2731
|
+
// Check if any parameter has raw_array flag
|
|
2732
|
+
let hasRawArrayBody = false;
|
|
2733
|
+
let rawBodyData = undefined;
|
|
2734
|
+
// Separate remaining parameters by location (query vs body)
|
|
2735
|
+
if (params[""] !== undefined) {
|
|
2736
|
+
bodyParams[""] = params[""];
|
|
2737
|
+
extractedParams.push("");
|
|
2738
|
+
}
|
|
2739
|
+
if (params[""] !== undefined) {
|
|
2740
|
+
bodyParams[""] = params[""];
|
|
2741
|
+
extractedParams.push("");
|
|
2742
|
+
}
|
|
2743
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
2744
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2745
|
+
if (!extractedParams.includes(key)) {
|
|
2746
|
+
bodyParams[key] = value;
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
// Validate required parameters
|
|
2750
|
+
const path = this.buildPath('/comments', pathParams);
|
|
2751
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
2752
|
+
const requestPath = path === '/' ? '' : path;
|
|
2753
|
+
// Report initial progress if callback provided
|
|
2754
|
+
if (options?.onProgress) {
|
|
2755
|
+
await options.onProgress({
|
|
2756
|
+
progress: 0,
|
|
2757
|
+
total: 100,
|
|
2758
|
+
message: `Starting create_comment request...`
|
|
2759
|
+
});
|
|
2760
|
+
}
|
|
2761
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
2762
|
+
const requestConfig = {};
|
|
2763
|
+
if (options?.signal) {
|
|
2764
|
+
requestConfig.signal = options.signal;
|
|
2765
|
+
}
|
|
2766
|
+
const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
|
|
2767
|
+
// Report completion progress if callback provided
|
|
2768
|
+
if (options?.onProgress) {
|
|
2769
|
+
await options.onProgress({
|
|
2770
|
+
progress: 100,
|
|
2771
|
+
total: 100,
|
|
2772
|
+
message: `Completed create_comment request`
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
const duration = Date.now() - startTime;
|
|
2776
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
2777
|
+
endpoint: 'create_comment',
|
|
2778
|
+
method: 'POST',
|
|
2779
|
+
path: '/comments',
|
|
2780
|
+
duration_ms: duration,
|
|
2781
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
2782
|
+
});
|
|
2783
|
+
return {
|
|
2784
|
+
content: [
|
|
2785
|
+
{
|
|
2786
|
+
type: 'text',
|
|
2787
|
+
text: JSON.stringify(response.data, null, 2)
|
|
2788
|
+
}
|
|
2789
|
+
]
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
catch (error) {
|
|
2793
|
+
const duration = Date.now() - startTime;
|
|
2794
|
+
// Check if error is due to cancellation
|
|
2795
|
+
if (axios.isCancel(error)) {
|
|
2796
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
2797
|
+
endpoint: 'create_comment',
|
|
2798
|
+
method: 'POST',
|
|
2799
|
+
path: '/comments',
|
|
2800
|
+
duration_ms: duration
|
|
2801
|
+
});
|
|
2802
|
+
throw new Error('Request was cancelled');
|
|
2803
|
+
}
|
|
2804
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
2805
|
+
endpoint: 'create_comment',
|
|
2806
|
+
method: 'POST',
|
|
2807
|
+
path: '/comments',
|
|
2808
|
+
duration_ms: duration,
|
|
2809
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2810
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
2811
|
+
});
|
|
2812
|
+
throw new Error(`Failed to execute create_comment: ${error instanceof Error ? error.message : String(error)}`);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
/* DEBUG: endpoint={"name":"get_comments","method":"GET","path":"/comments","description":"Get comments for a page or block","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to get comments for","location":"query"},"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"query"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"query","default":100}},"response_format":"json","category":"Page Creation & Editing"} */
|
|
2816
|
+
async getComments(params, options) {
|
|
2817
|
+
const startTime = Date.now();
|
|
2818
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2819
|
+
endpoint: 'get_comments',
|
|
2820
|
+
method: 'GET',
|
|
2821
|
+
path: '/comments',
|
|
2822
|
+
paramCount: Object.keys(params || {}).length,
|
|
2823
|
+
paramKeys: Object.keys(params || {})
|
|
2824
|
+
});
|
|
2825
|
+
try {
|
|
2826
|
+
// Extract and separate parameters by location: path, query, body
|
|
2827
|
+
const pathTemplate = '/comments';
|
|
2828
|
+
const pathParams = {};
|
|
2829
|
+
const queryParams = {};
|
|
2830
|
+
const bodyParams = {};
|
|
2831
|
+
const extractedParams = [];
|
|
2832
|
+
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
2833
|
+
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
2834
|
+
let match;
|
|
2835
|
+
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
2836
|
+
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
2837
|
+
if (paramName && params[paramName] !== undefined) {
|
|
2838
|
+
pathParams[paramName] = params[paramName];
|
|
2839
|
+
extractedParams.push(paramName);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
// Handle standard path templates: {resourceName}
|
|
2843
|
+
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
2844
|
+
standardPathParams.forEach(paramTemplate => {
|
|
2845
|
+
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
2846
|
+
// Only process if not already handled by Google template logic
|
|
2847
|
+
if (!extractedParams.includes(paramName)) {
|
|
2848
|
+
if (params[paramName] !== undefined) {
|
|
2849
|
+
pathParams[paramName] = params[paramName];
|
|
2850
|
+
extractedParams.push(paramName);
|
|
2851
|
+
}
|
|
2852
|
+
else {
|
|
2853
|
+
// Provide default values for optional path parameters
|
|
2854
|
+
if (paramName === 'userId') {
|
|
2855
|
+
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
2856
|
+
extractedParams.push(paramName);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
// Check if any parameter has raw_array flag
|
|
2862
|
+
let hasRawArrayBody = false;
|
|
2863
|
+
let rawBodyData = undefined;
|
|
2864
|
+
// Separate remaining parameters by location (query vs body)
|
|
2865
|
+
if (params[""] !== undefined) {
|
|
2866
|
+
queryParams[""] = params[""];
|
|
2867
|
+
extractedParams.push("");
|
|
2868
|
+
}
|
|
2869
|
+
if (params[""] !== undefined) {
|
|
2870
|
+
queryParams[""] = params[""];
|
|
2871
|
+
extractedParams.push("");
|
|
2872
|
+
}
|
|
2873
|
+
if (params[""] !== undefined) {
|
|
2874
|
+
queryParams[""] = params[""];
|
|
2875
|
+
extractedParams.push("");
|
|
2876
|
+
}
|
|
2877
|
+
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
2878
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2879
|
+
if (!extractedParams.includes(key)) {
|
|
2880
|
+
bodyParams[key] = value;
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
// Validate required parameters
|
|
2884
|
+
const path = this.buildPath('/comments', pathParams);
|
|
2885
|
+
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
2886
|
+
const requestPath = path === '/' ? '' : path;
|
|
2887
|
+
// Report initial progress if callback provided
|
|
2888
|
+
if (options?.onProgress) {
|
|
2889
|
+
await options.onProgress({
|
|
2890
|
+
progress: 0,
|
|
2891
|
+
total: 100,
|
|
2892
|
+
message: `Starting get_comments request...`
|
|
2893
|
+
});
|
|
2894
|
+
}
|
|
2895
|
+
// Use standard HTTP client for other auth types with abort signal
|
|
2896
|
+
const requestConfig = {};
|
|
2897
|
+
if (options?.signal) {
|
|
2898
|
+
requestConfig.signal = options.signal;
|
|
2899
|
+
}
|
|
2900
|
+
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
2901
|
+
// Report completion progress if callback provided
|
|
2902
|
+
if (options?.onProgress) {
|
|
2903
|
+
await options.onProgress({
|
|
2904
|
+
progress: 100,
|
|
2905
|
+
total: 100,
|
|
2906
|
+
message: `Completed get_comments request`
|
|
2907
|
+
});
|
|
2908
|
+
}
|
|
2909
|
+
const duration = Date.now() - startTime;
|
|
2910
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
2911
|
+
endpoint: 'get_comments',
|
|
2912
|
+
method: 'GET',
|
|
2913
|
+
path: '/comments',
|
|
2914
|
+
duration_ms: duration,
|
|
2915
|
+
responseDataSize: JSON.stringify(response.data).length
|
|
2916
|
+
});
|
|
2917
|
+
return {
|
|
2918
|
+
content: [
|
|
2919
|
+
{
|
|
2920
|
+
type: 'text',
|
|
2921
|
+
text: JSON.stringify(response.data, null, 2)
|
|
2922
|
+
}
|
|
2923
|
+
]
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
catch (error) {
|
|
2927
|
+
const duration = Date.now() - startTime;
|
|
2928
|
+
// Check if error is due to cancellation
|
|
2929
|
+
if (axios.isCancel(error)) {
|
|
2930
|
+
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
2931
|
+
endpoint: 'get_comments',
|
|
2932
|
+
method: 'GET',
|
|
2933
|
+
path: '/comments',
|
|
2934
|
+
duration_ms: duration
|
|
2935
|
+
});
|
|
2936
|
+
throw new Error('Request was cancelled');
|
|
2937
|
+
}
|
|
2938
|
+
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
2939
|
+
endpoint: 'get_comments',
|
|
2940
|
+
method: 'GET',
|
|
2941
|
+
path: '/comments',
|
|
2942
|
+
duration_ms: duration,
|
|
2943
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2944
|
+
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
2945
|
+
});
|
|
2946
|
+
throw new Error(`Failed to execute get_comments: ${error instanceof Error ? error.message : String(error)}`);
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
//# sourceMappingURL=notion-client.js.map
|