UNPKG

@google/clasp

Version:

Develop Apps Script Projects locally

180 lines (179 loc) 8.33 kB
import path from 'path'; import Debug from 'debug'; import fs from 'fs/promises'; import { google } from 'googleapis'; import { PUBLIC_ADVANCED_SERVICES } from './apis.js'; import { assertGcpProjectConfigured, handleApiError } from './utils.js'; import { fetchWithPages } from './utils.js'; const debug = Debug('clasp:core'); export class Services { constructor(config) { this.options = config; } async getEnabledServices() { debug('Fetching enabled services'); assertGcpProjectConfigured(this.options); const projectId = this.options.project.projectId; const serviceUsage = google.serviceusage({ version: 'v1', auth: this.options.credentials }); try { const serviceList = await fetchWithPages(async (pageSize, pageToken) => { var _a, _b; const requestOptions = { parent: `projects/${projectId}`, filter: 'state:ENABLED', pageSize, pageToken, }; debug('Fetching available APIs with request %O', requestOptions); const res = await serviceUsage.services.list(requestOptions); return { results: (_a = res.data.services) !== null && _a !== void 0 ? _a : [], pageToken: (_b = res.data.nextPageToken) !== null && _b !== void 0 ? _b : undefined, }; }, { pageSize: 200, maxResults: 10000, }); // Filter out the disabled ones. Print the enabled ones. const truncateName = (name) => { const i = name.indexOf('.'); if (i !== -1) { return name.slice(0, i); } return name; }; const allowedIds = PUBLIC_ADVANCED_SERVICES.map(service => service.serviceId); return serviceList.results .map(service => { var _a, _b, _c, _d, _e, _f; return ({ id: (_a = service.name) !== null && _a !== void 0 ? _a : '', name: truncateName((_c = (_b = service.config) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : 'Unknown name'), description: (_f = (_e = (_d = service.config) === null || _d === void 0 ? void 0 : _d.documentation) === null || _e === void 0 ? void 0 : _e.summary) !== null && _f !== void 0 ? _f : '', }); }) .filter(service => { return allowedIds.indexOf(service.name) !== -1; }); } catch (error) { handleApiError(error); } } async getAvailableServices() { var _a; debug('Fetching available services'); const discovery = google.discovery({ version: 'v1' }); try { const { data } = await discovery.apis.list({ preferred: true, }); const allowedIds = PUBLIC_ADVANCED_SERVICES.map(service => service.serviceId); const allServices = (_a = data.items) !== null && _a !== void 0 ? _a : []; const isValidService = (s) => { return (s.id !== undefined && s.name !== undefined && allowedIds.indexOf(s.name) !== -1 && s.description !== undefined); }; const services = allServices.filter(isValidService).sort((a, b) => a.id.localeCompare(b.id)); debug('Available services: %O', services); return services; } catch (error) { handleApiError(error); } } async enableService(serviceName) { var _a; debug('Enabling service %s', serviceName); assertGcpProjectConfigured(this.options); const projectId = this.options.project.projectId; const contentDir = this.options.files.contentDir; if (!serviceName) { throw new Error('No service name provided.'); } const manifestPath = path.join(contentDir, 'appsscript.json'); const manifestExists = await hasReadWriteAccess(manifestPath); if (!manifestExists) { debug('Manifest file at %s does not exist', manifestPath); throw new Error('Manifest file does not exist.'); } const advancedService = PUBLIC_ADVANCED_SERVICES.find(service => service.serviceId === serviceName); if (!advancedService) { throw new Error('Service is not a valid advanced service.'); } // Do not update manifest if not valid advanced service debug('Service is an advanced service, updating manifest'); const content = await fs.readFile(manifestPath); const manifest = JSON.parse(content.toString()); if ((_a = manifest.dependencies) === null || _a === void 0 ? void 0 : _a.enabledAdvancedServices) { if (manifest.dependencies.enabledAdvancedServices.findIndex(s => s.userSymbol === advancedService.userSymbol) === -1) { manifest.dependencies.enabledAdvancedServices.push(advancedService); } } else if (manifest.dependencies) { manifest.dependencies.enabledAdvancedServices = [advancedService]; } else { manifest.dependencies = { enabledAdvancedServices: [advancedService] }; } debug('Updating manifest at %s with %j', manifestPath, manifest); await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2)); debug('Enabling GCP service %s.googleapis.com', serviceName); const serviceUsage = google.serviceusage({ version: 'v1', auth: this.options.credentials }); const resourceName = `projects/${projectId}/services/${serviceName}.googleapis.com`; try { await serviceUsage.services.enable({ name: resourceName }); } catch (error) { handleApiError(error); } } async disableService(serviceName) { var _a; debug('Disabling service %s', serviceName); assertGcpProjectConfigured(this.options); const projectId = this.options.project.projectId; const contentDir = this.options.files.contentDir; if (!serviceName) { throw new Error('No service name provided.'); } const manifestPath = path.join(contentDir, 'appsscript.json'); const manifestExists = await hasReadWriteAccess(manifestPath); if (!manifestExists) { debug('Manifest file at %s does not exist', manifestPath); throw new Error('Manifest file does not exist.'); } const advancedService = PUBLIC_ADVANCED_SERVICES.find(service => service.serviceId === serviceName); if (!advancedService) { throw new Error('Service is not a valid advanced service.'); } // Do not update manifest if not valid advanced service debug('Service is an advanced service, updating manifest'); const content = await fs.readFile(manifestPath); const manifest = JSON.parse(content.toString()); if (!((_a = manifest.dependencies) === null || _a === void 0 ? void 0 : _a.enabledAdvancedServices)) { debug('Service enabled in manifest, skipping manifest update'); return; } manifest.dependencies.enabledAdvancedServices = manifest.dependencies.enabledAdvancedServices.filter(service => service.serviceId !== serviceName); debug('Updating manifest at %s with %j', manifestPath, manifest); await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2)); debug('Disabling GCP service %s.googleapis.com', serviceName); const serviceUsage = google.serviceusage({ version: 'v1', auth: this.options.credentials }); const resourceName = `projects/${projectId}/services/${serviceName}.googleapis.com`; try { await serviceUsage.services.disable({ name: resourceName }); } catch (error) { handleApiError(error); } } } async function hasReadWriteAccess(path) { try { await fs.access(path, fs.constants.W_OK | fs.constants.R_OK); } catch { return false; } return true; }