@google/clasp
Version:
Develop Apps Script Projects locally
180 lines (179 loc) • 8.33 kB
JavaScript
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;
}