UNPKG

@coretext-ai/qa-google-contacts-a58500d5-8331-4ce9-a140-d204a9fae815

Version:
615 lines 23.7 kB
import { GoogleOAuthClient } from '../oauth/google-oauth-client.js'; export class GoogleContactsClient { constructor() { this.baseUrl = 'https://people.googleapis.com'; // Generate unique session ID for this client instance this.sessionId = `google-contacts-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`; this.logDebug('INIT', 'OAuth client instance created', { baseUrl: this.baseUrl, isOAuth: true }); this.oauthClient = new GoogleOAuthClient(); } /** * Get the session ID for this client instance */ getSessionId() { return this.sessionId; } /** * Enhanced debug logging with session context */ logDebug(action, message, metadata) { const timestamp = new Date().toISOString(); const user = process.env.CORETEXT_USER || 'unknown'; const logEntry = { timestamp, sessionId: this.sessionId, user, integration: 'google-contacts', component: 'oauth-client', action, message, ...(metadata && { metadata }) }; // Use stderr to avoid MCP protocol interference console.error(`[GOOGLE_CONTACTS-OAUTH-CLIENT] ${JSON.stringify(logEntry)}`); } /** * Initialize the API client */ async initialize() { this.logDebug('INITIALIZE', 'Starting OAuth client initialization', { hasOAuthClient: true }); await this.oauthClient.initialize(); this.logDebug('INITIALIZE', 'Google OAuth client initialization completed'); } /** * Make authenticated API request */ async makeRequest(config) { const startTime = Date.now(); this.logDebug('REQUEST_START', 'Making authenticated API request', { method: config.method, path: config.path, hasBody: !!config.body, hasQueryParams: !!config.queryParams }); const accessToken = await this.oauthClient.getValidAccessToken(); this.logDebug('AUTH_TOKEN', 'Retrieved Google OAuth access token', { tokenPreview: accessToken ? accessToken.substring(0, 8) + '...' : 'none' }); const headers = { 'Accept': 'application/json', 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', ...config.headers }; const url = this.buildUrl(config.path, config.pathParams, config.queryParams); const response = await fetch(url, { method: config.method, headers, body: config.body ? JSON.stringify(config.body) : undefined }); if (!response.ok) { if (response.status === 401) { // Token might be invalid, try to refresh and retry once await this.oauthClient.getValidAccessToken(); // This will refresh if needed return this.makeRequest(config); // Retry once } const errorText = await response.text(); throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`); } return response.json(); } /** * Create a new contact with specified fields */ async createContact(args = {}) { const pathParams = {}; const queryParams = { personFields: args.personFields, ...(args.sources !== undefined && { sources: args.sources }), }; const bodyParams = { ...(args.names !== undefined && { names: args.names }), ...(args.emailAddresses !== undefined && { emailAddresses: args.emailAddresses }), ...(args.phoneNumbers !== undefined && { phoneNumbers: args.phoneNumbers }), ...(args.addresses !== undefined && { addresses: args.addresses }), ...(args.organizations !== undefined && { organizations: args.organizations }), ...(args.biographies !== undefined && { biographies: args.biographies }), ...(args.birthdays !== undefined && { birthdays: args.birthdays }), ...(args.urls !== undefined && { urls: args.urls }), }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/v1/people:createContact', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Get a specific contact by resource name */ async getPerson(args = {}) { const pathParams = { resourceName: args.resourceName, }; const queryParams = { personFields: args.personFields, ...(args.sources !== undefined && { sources: args.sources }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/{resourceName}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Update an existing contact */ async updateContact(args = {}) { const pathParams = { resourceName: args.resourceName, }; const queryParams = { updatePersonFields: args.updatePersonFields, ...(args.personFields !== undefined && { personFields: args.personFields }), ...(args.sources !== undefined && { sources: args.sources }), }; const bodyParams = { ...(args.names !== undefined && { names: args.names }), ...(args.emailAddresses !== undefined && { emailAddresses: args.emailAddresses }), ...(args.phoneNumbers !== undefined && { phoneNumbers: args.phoneNumbers }), ...(args.addresses !== undefined && { addresses: args.addresses }), ...(args.organizations !== undefined && { organizations: args.organizations }), ...(args.biographies !== undefined && { biographies: args.biographies }), ...(args.birthdays !== undefined && { birthdays: args.birthdays }), ...(args.urls !== undefined && { urls: args.urls }), ...(args.etag !== undefined && { etag: args.etag }), }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'PATCH', path: '/v1/{resourceName}:updateContact', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Delete a contact permanently */ async deleteContact(args = {}) { const pathParams = { resourceName: args.resourceName, }; const queryParams = {}; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'DELETE', path: '/v1/{resourceName}:deleteContact', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * List authenticated user's contacts */ async listConnections(args = {}) { const pathParams = {}; const queryParams = { ...(args.pageSize !== undefined && { pageSize: args.pageSize }), ...(args.pageToken !== undefined && { pageToken: args.pageToken }), personFields: args.personFields, ...(args.sortOrder !== undefined && { sortOrder: args.sortOrder }), ...(args.sources !== undefined && { sources: args.sources }), ...(args.syncToken !== undefined && { syncToken: args.syncToken }), ...(args.requestSyncToken !== undefined && { requestSyncToken: args.requestSyncToken }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/people/me/connections', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Search across all contacts with text query */ async searchContacts(args = {}) { const pathParams = {}; const queryParams = { query: args.query, ...(args.pageSize !== undefined && { pageSize: args.pageSize }), ...(args.readMask !== undefined && { readMask: args.readMask }), ...(args.sources !== undefined && { sources: args.sources }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/people:searchContacts', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Get multiple contacts by resource names */ async batchGetPeople(args = {}) { const pathParams = {}; const queryParams = { resourceNames: args.resourceNames, personFields: args.personFields, ...(args.sources !== undefined && { sources: args.sources }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/people:batchGet', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * List all contact groups */ async listContactGroups(args = {}) { const pathParams = {}; const queryParams = { ...(args.pageSize !== undefined && { pageSize: args.pageSize }), ...(args.pageToken !== undefined && { pageToken: args.pageToken }), ...(args.groupFields !== undefined && { groupFields: args.groupFields }), ...(args.syncToken !== undefined && { syncToken: args.syncToken }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/contactGroups', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Create a new contact group */ async createContactGroup(args = {}) { const pathParams = {}; const queryParams = { ...(args.readGroupFields !== undefined && { readGroupFields: args.readGroupFields }), }; const bodyParams = { contactGroup: args.contactGroup, }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/v1/contactGroups', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Get a specific contact group by resource name */ async getContactGroup(args = {}) { const pathParams = { resourceName: args.resourceName, }; const queryParams = { ...(args.maxMembers !== undefined && { maxMembers: args.maxMembers }), ...(args.groupFields !== undefined && { groupFields: args.groupFields }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/{resourceName}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Update an existing contact group */ async updateContactGroup(args = {}) { const pathParams = { resourceName: args.resourceName, }; const queryParams = { ...(args.readGroupFields !== undefined && { readGroupFields: args.readGroupFields }), }; const bodyParams = { contactGroup: args.contactGroup, }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'PUT', path: '/v1/{resourceName}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Delete a contact group */ async deleteContactGroup(args = {}) { const pathParams = { resourceName: args.resourceName, }; const queryParams = { ...(args.deleteContacts !== undefined && { deleteContacts: args.deleteContacts }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'DELETE', path: '/v1/{resourceName}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Add or remove members from a contact group */ async modifyContactGroupMembers(args = {}) { const pathParams = { resourceName: args.resourceName, }; const queryParams = {}; const bodyParams = { ...(args.resourceNamesToAdd !== undefined && { resourceNamesToAdd: args.resourceNamesToAdd }), ...(args.resourceNamesToRemove !== undefined && { resourceNamesToRemove: args.resourceNamesToRemove }), }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/v1/{resourceName}/members:modify', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * List people in the authenticated user's domain directory (G Suite/Workspace) */ async listDirectoryPeople(args = {}) { const pathParams = {}; const queryParams = { ...(args.pageSize !== undefined && { pageSize: args.pageSize }), ...(args.pageToken !== undefined && { pageToken: args.pageToken }), readMask: args.readMask, sources: args.sources, ...(args.mergeSources !== undefined && { mergeSources: args.mergeSources }), ...(args.syncToken !== undefined && { syncToken: args.syncToken }), ...(args.requestSyncToken !== undefined && { requestSyncToken: args.requestSyncToken }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/people:listDirectoryPeople', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Search people in the authenticated user's domain directory */ async searchDirectoryPeople(args = {}) { const pathParams = {}; const queryParams = { query: args.query, ...(args.pageSize !== undefined && { pageSize: args.pageSize }), ...(args.pageToken !== undefined && { pageToken: args.pageToken }), readMask: args.readMask, sources: args.sources, ...(args.mergeSources !== undefined && { mergeSources: args.mergeSources }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/people:searchDirectoryPeople', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * List other contacts (auto-created contacts from interactions) */ async listOtherContacts(args = {}) { const pathParams = {}; const queryParams = { ...(args.pageSize !== undefined && { pageSize: args.pageSize }), ...(args.pageToken !== undefined && { pageToken: args.pageToken }), readMask: args.readMask, ...(args.sources !== undefined && { sources: args.sources }), ...(args.syncToken !== undefined && { syncToken: args.syncToken }), ...(args.requestSyncToken !== undefined && { requestSyncToken: args.requestSyncToken }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/otherContacts', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Search other contacts with text query */ async searchOtherContacts(args = {}) { const pathParams = {}; const queryParams = { query: args.query, ...(args.pageSize !== undefined && { pageSize: args.pageSize }), ...(args.readMask !== undefined && { readMask: args.readMask }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/v1/otherContacts:search', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Copy an other contact to the authenticated user's contacts */ async copyOtherContact(args = {}) { const pathParams = { resourceName: args.resourceName, }; const queryParams = {}; const bodyParams = { copyMask: args.copyMask, ...(args.sources !== undefined && { sources: args.sources }), }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/v1/{resourceName}:copyOtherContactToMyContactsGroup', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Build URL with path parameters and query string */ buildUrl(path, pathParams, queryParams) { let url = this.baseUrl + path; // Replace path parameters if (pathParams) { for (const [key, value] of Object.entries(pathParams)) { url = url.replace(`{${key}}`, encodeURIComponent(String(value))); } } // Add query parameters if (queryParams && Object.keys(queryParams).length > 0) { const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(queryParams)) { if (value !== undefined && value !== null) { searchParams.append(key, String(value)); } } const queryString = searchParams.toString(); if (queryString) { url += `?${queryString}`; } } return url; } /** * Check if client is authenticated */ async isAuthenticated() { return this.oauthClient.isAuthenticated(); } /** * Revoke authentication tokens */ async revokeAuthentication() { await this.oauthClient.revokeTokens(); } } //# sourceMappingURL=google-contacts-client.js.map