UNPKG

aws-appsync-vtl

Version:

Push to and pull from AWS AppSync velocity templates of your resolvers

239 lines (238 loc) 10.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Synchronizer = void 0; const promises_1 = require("fs/promises"); const path_1 = __importDefault(require("path")); const inquirer_1 = __importDefault(require("inquirer")); const client_appsync_1 = require("@aws-sdk/client-appsync"); class Synchronizer { constructor(config) { this.pull = async (alwaysYes = false) => { this.createIfMissing(this.resolversPath); const { resolvers } = await this.appSync.send(new client_appsync_1.ListResolversCommand({ apiId: this.apiId, typeName: 'Query', })); if (resolvers) { for (const resolver of resolvers) { this.pullResolver(resolver, alwaysYes); } } }; this.deleteType = async () => { const typeNames = await promises_1.readdir(this.resolversPath); if (typeNames.length === 0) { console.log('Nothing to do'); return; } const { selection } = await inquirer_1.default.prompt([ { name: 'selection', type: 'checkbox', message: 'Choose type', choices: typeNames, }, ]); for (const type of selection) { const typePath = path_1.default.join(this.resolversPath, type); const resolvers = await promises_1.readdir(typePath); for (const resolver of resolvers) { await this.deleteResolverAt(type, resolver); } await promises_1.rmdir(typePath); console.log(`Removed ${type}`); } }; this.deleteResolver = async () => { const typeNames = await promises_1.readdir(this.resolversPath); if (typeNames.length === 0) { console.log('Nothing to do'); return; } const { type } = await inquirer_1.default.prompt([ { name: 'type', type: 'list', message: 'Choose type', choices: typeNames, }, ]); const typePath = path_1.default.join(this.resolversPath, type); const resolvers = await promises_1.readdir(typePath); if (resolvers.length === 0) { console.log(`No resolvers for the ${type} found`); return; } const { resolverSelections } = await inquirer_1.default.prompt([ { name: 'resolverSelections', type: 'checkbox', message: 'Choose type', choices: resolvers, }, ]); for (const resolver of resolverSelections) { await this.deleteResolverAt(type, resolver); } }; this.deleteResolverAt = async (type, resolver) => { const resolverPath = path_1.default.join(this.resolversPath, type, resolver); const metaPath = path_1.default.join(resolverPath, 'meta.json'); const meta = await this.exists(metaPath) ? JSON.parse(await promises_1.readFile(metaPath, 'utf-8')) : undefined; if (meta) { await this.appSync.send(new client_appsync_1.DeleteResolverCommand({ apiId: this.apiId, typeName: type, fieldName: resolver, })); } await promises_1.rm(resolverPath, { recursive: true }); console.log(`Removed ${type}.${resolver}`); }; this.push = async () => { const typeNames = await promises_1.readdir(this.resolversPath); const dataSources = (await this.getDataSources()).dataSources.map((ds) => ds.name); for (const type of typeNames) { const typePath = path_1.default.join(this.resolversPath, type); const fieldNames = await promises_1.readdir(typePath); for (const fieldName of fieldNames) { const resolverPath = path_1.default.join(typePath, fieldName); const metaPath = path_1.default.join(resolverPath, 'meta.json'); const requestPath = path_1.default.join(resolverPath, 'request.vm'); const responsePath = path_1.default.join(resolverPath, 'response.vm'); const responseTemplateExists = await this.exists(requestPath); const requestTemplateExists = await this.exists(requestPath); if (!requestTemplateExists && !responseTemplateExists) { // eslint-disable-next-line no-continue continue; } let ds = await this.exists(metaPath) ? JSON.parse(await promises_1.readFile(metaPath, 'utf-8')).dataSourceName : ''; const creating = !ds; const info = creating ? 'Creating' : 'Updating'; if (creating) { ds = (await inquirer_1.default.prompt([ { name: 'ds', type: 'list', message: `Please set data source for ${type}.${fieldName}`, choices: dataSources, }, ])).ds; } console.log(`${info} resolver: ${type}.${fieldName}`); const requestMappingTemplate = await this.exists(requestPath) ? await promises_1.readFile(requestPath, 'utf-8') : undefined; const responseMappingTemplate = await this.exists(responsePath) ? await promises_1.readFile(responsePath, 'utf-8') : undefined; const input = { apiId: this.apiId, typeName: type, fieldName, dataSourceName: ds, requestMappingTemplate, responseMappingTemplate, }; try { const resp = await this.appSync.send(creating ? new client_appsync_1.CreateResolverCommand(input) : new client_appsync_1.UpdateResolverCommand(input)); await promises_1.writeFile(metaPath, JSON.stringify(resp.resolver, null, 2)); } catch (e) { console.error(e); } } } }; this.pullResolver = async (resolver, alwaysYes) => { if (!resolver.requestMappingTemplate && !resolver.responseMappingTemplate) { // Nothing to do return; } console.log(`Processing resolver: ${resolver.typeName}.${resolver.fieldName}`); const resolverPath = path_1.default.join(this.resolversPath, resolver.typeName, resolver.fieldName); await this.createIfMissing(resolverPath); if (resolver.requestMappingTemplate) { await this.processResolverTemplate(path_1.default.join(resolverPath, 'request.vm'), resolver.requestMappingTemplate, alwaysYes); } if (resolver.responseMappingTemplate) { await this.processResolverTemplate(path_1.default.join(resolverPath, 'response.vm'), resolver.responseMappingTemplate, alwaysYes); } await promises_1.writeFile(path_1.default.join(resolverPath, 'meta.json'), JSON.stringify(resolver, null, 2)); }; this.processResolverTemplate = async (templatePath, template, alwaysYes) => { const exists = await this.exists(templatePath); if (exists && !alwaysYes) { const choice = await inquirer_1.default.prompt([ { name: 'override', type: 'list', message: `Template ${templatePath} already exists. Would you like to override it?`, choices: [ 'Yes', 'No', ], }, ]); if (choice.override === 'No') { return; } } await promises_1.writeFile(templatePath, template); }; this.getDataSources = async () => this.appSync.send(new client_appsync_1.ListDataSourcesCommand({ apiId: this.apiId, })); this.exists = async (path) => { try { await promises_1.access(path); } catch (e) { if (e.code === 'ENOENT') { return false; } throw e; } return true; }; this.createIfMissing = async (path) => { try { await promises_1.access(path); } catch (e) { if (e.code === 'ENOENT') { await promises_1.mkdir(path, { recursive: true }); return true; } throw e; } return false; }; this.apiId = config.apiId; this.resolversPath = config.resolversPath; this.appSync = new client_appsync_1.AppSyncClient({}); this.appSync.middlewareStack.add((next) => async (args) => { // eslint-disable-next-line no-param-reassign args.request.path = `/v1${args.request.path}`; return next(args); }, { name: 'path fixup', step: 'build', priority: 'high' }); } } exports.Synchronizer = Synchronizer; Synchronizer.create = () => { // eslint-disable-next-line global-require const dotenv = require('dotenv'); dotenv.config(); if (!process.env.API_ID) { throw new Error('process.env.API_ID must be set'); } if (!process.env.RESOLVERS_PATH) { throw new Error('process.env.RESOLVERS_PATH must be set'); } return new Synchronizer({ apiId: process.env.API_ID, resolversPath: process.env.RESOLVERS_PATH, }); };