fauna-gql-upload
Version:
Manage your FaunaDB resources in within your project and upload them using a single command
535 lines (534 loc) • 27.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
exports.__esModule = true;
var fs_1 = __importDefault(require("fs"));
var path_1 = __importDefault(require("path"));
var getConfig_1 = __importStar(require("../util/getConfig"));
var logger_1 = require("../util/logger");
var prompts_1 = __importDefault(require("prompts"));
var child_process_1 = require("child_process");
var dotenv_1 = __importDefault(require("dotenv"));
var error_1 = __importDefault(require("../util/error"));
var templatesDir = path_1["default"].join(__dirname, "..", "..", "templates");
var defaultCommand = "fauna";
var fguPkgName = "fauna-gql-upload";
var faunaPkgName = "faunadb";
var setupQuestions = [
{
type: "text",
name: "secret",
message: "Secret admin key for your database (this will be added to your .env file):",
validate: function (value) { return !!value || "Secret is required to upload resources, enter a valid admin secret key."; }
},
{
type: "confirm",
name: "development",
message: "Do you want FGU to connect to a local development instance of FaunaDB?",
initial: false
},
{
type: "select",
name: "region",
message: "Select the region for your production database:",
choices: [
{ title: "classic", value: "classic" },
{ title: "us", value: "us" },
{ title: "eu", value: "eu" },
{ title: "preview", value: "preview" },
{ title: "auto", value: "auto", selected: true, description: "Use whatever region works with your secret key" }
]
},
{
type: "text",
name: "command",
message: "Name of npm script to upload resources:",
initial: defaultCommand
},
{
type: "confirm",
name: "resources",
message: "Would you like to create placeholder resources?",
initial: true
}
];
var configOptions = {
"$schema": "node_modules/fauna-gql-upload/config.schema.json"
};
var resourceDirs = new Map([
["schema", getConfig_1.argv.schemaDir || "schema"],
["data", getConfig_1.argv.dataDir || "data"],
["functions", getConfig_1.argv.fnsDir || "functions"],
["indexes", getConfig_1.argv.indexesDir || "indexes"],
["providers", getConfig_1.argv.providersDir || "providers"],
["roles", getConfig_1.argv.rolesDir || "roles"]
]);
/** Setup FGU files and folders based on configuration questions. */
function init() {
return __awaiter(this, void 0, void 0, function () {
var yes, customConfig, configPath, configFileExists, resources, concatSchema, setup, resourcesAnswers, envFile, envFilePath, envJson;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
yes = getConfig_1.argv.yes;
customConfig = getConfig_1.argv.config;
configPath = customConfig || path_1["default"].join(process.cwd(), ".fauna.json");
configFileExists = fs_1["default"].existsSync(configPath);
resources = ["functions", "indexes", "schema"];
concatSchema = false;
setup = {
command: defaultCommand,
secret: undefined,
resources: true,
development: false,
region: "auto"
};
if (configFileExists) {
(0, error_1["default"])("The current directory already contains a `.fauna.json` file. Cannot run init in an already initialized directory.");
}
(0, logger_1.status)("Initializing FGU in the current directory...", "info");
if (!!yes) return [3 /*break*/, 6];
return [4 /*yield*/, (0, prompts_1["default"])(setupQuestions)];
case 1:
setup = _a.sent();
if (!setup.resources) return [3 /*break*/, 5];
return [4 /*yield*/, (0, prompts_1["default"])({
type: "multiselect",
name: "resources",
message: "Select resources to create:",
choices: [
{ title: "Schema", value: "schema", selected: true },
{ title: "Functions", value: "functions", selected: true },
{ title: "Roles", value: "roles", selected: false },
{ title: "Providers", value: "providers", selected: false },
{ title: "Indexes", value: "indexes", selected: true },
{ title: "Data", value: "data", selected: false }
]
})];
case 2:
resourcesAnswers = _a.sent();
resources = resourcesAnswers.resources;
if (!resourcesAnswers.resources.includes("schema")) return [3 /*break*/, 4];
return [4 /*yield*/, (0, prompts_1["default"])({
name: "concatSchema",
type: "confirm",
message: "Would you like to enable schema concatenation?",
initial: false
})];
case 3:
concatSchema = (_a.sent()).concatSchema;
_a.label = 4;
case 4:
// If concatenation is enabled, add schemaDir to config.
if (concatSchema)
configOptions.schemaDir = path_1["default"].join("fauna", "schema");
return [3 /*break*/, 6];
case 5:
// User does not want to create resources, so set resources to empty array.
resources = [];
_a.label = 6;
case 6:
if (yes) {
envFile = getConfig_1.argv.envPath || ".env";
try {
envFilePath = path_1["default"].join(process.cwd(), envFile);
envJson = dotenv_1["default"].parse(fs_1["default"].readFileSync(envFilePath));
setup.secret = envJson[getConfig_1.argv.secretEnv || getConfig_1.defaultSecretEnv];
}
catch (_b) {
(0, error_1["default"])("No ".concat(envFile, " file found in the current directory. Please create one and add the admin key to it."));
}
}
if (!setup.secret) {
(0, error_1["default"])("Secret key was not provided, cannot finish initialization.");
}
if (setup.region !== "auto") {
configOptions.region = setup.region;
}
return [4 /*yield*/, createPackage(setup.command, customConfig)];
case 7:
_a.sent();
return [4 /*yield*/, updateEnvFile(setup)];
case 8:
_a.sent();
return [4 /*yield*/, createResources(resources, concatSchema)];
case 9:
_a.sent();
return [4 /*yield*/, createConfigFile(configPath, configOptions)];
case 10:
_a.sent();
(0, logger_1.status)("Fauna GQL Upload has been initialized. To upload resources, run: `npm run ".concat(setup.command, "`"), "success");
return [2 /*return*/];
}
});
});
}
exports["default"] = init;
function addCustomOptions(configOptions) {
var acceptedOptions = Object.keys((0, getConfig_1["default"])()).filter(function (opt) { return opt !== "codegen"; });
Object.keys(acceptedOptions).forEach(function (key) {
if (getConfig_1.argv[key]) {
//@ts-ignore
configOptions[key] = getConfig_1.argv[key];
}
});
}
/** Create a config file using the provided options. */
function createConfigFile(path, config) {
return __awaiter(this, void 0, void 0, function () {
var configData;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, logger_1.status)("Creating config file at ".concat(path, "..."), "info");
configData = JSON.stringify(config, null, 2);
return [4 /*yield*/, fs_1["default"].promises.writeFile(path, configData)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
}
function createPackage(script, customConfig) {
return __awaiter(this, void 0, void 0, function () {
var packagePath, packageExists, packageFile, packageJson, fguInstalled, fdbInstalled, packagesToInstall;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
packagePath = path_1["default"].join(process.cwd(), "package.json");
packageExists = fs_1["default"].existsSync(packagePath);
if (!!packageExists) return [3 /*break*/, 3];
(0, logger_1.status)("Initializing package.json...", "info");
return [4 /*yield*/, new Promise(function (resolve) {
return (0, child_process_1.exec)("npm init -y", function (error) {
if (error)
throw error;
resolve(undefined);
});
})];
case 1:
_a.sent();
// Install fauna-gql-upload and faunadb
return [4 /*yield*/, installPackages([
{
packageName: faunaPkgName,
dev: false
},
{
packageName: fguPkgName,
dev: true
}
])];
case 2:
// Install fauna-gql-upload and faunadb
_a.sent();
_a.label = 3;
case 3: return [4 /*yield*/, fs_1["default"].promises.readFile(packagePath, "utf8")];
case 4:
packageFile = _a.sent();
packageJson = JSON.parse(packageFile);
fguInstalled = !!packageJson.devDependencies[fguPkgName];
fdbInstalled = !!packageJson.dependencies[faunaPkgName];
if (!(!fguInstalled || !fdbInstalled)) return [3 /*break*/, 7];
packagesToInstall = [];
if (!fdbInstalled)
packagesToInstall.push({
packageName: faunaPkgName,
dev: false
});
if (!fguInstalled)
packagesToInstall.push({
packageName: fguPkgName,
dev: true
});
return [4 /*yield*/, installPackages(packagesToInstall)];
case 5:
_a.sent();
return [4 /*yield*/, fs_1["default"].promises.readFile(packagePath, "utf8")];
case 6:
// Read package.json again
packageFile = _a.sent();
packageJson = JSON.parse(packageFile);
_a.label = 7;
case 7:
// Add upload script to package.json
packageJson.scripts[script] = "fgu upload ".concat(customConfig ? "--config ".concat(customConfig) : "");
// Write package.json
return [4 /*yield*/, fs_1["default"].promises.writeFile(packagePath, JSON.stringify(packageJson, null, 2))];
case 8:
// Write package.json
_a.sent();
return [2 /*return*/];
}
});
});
}
function installPackages(packages) {
return __awaiter(this, void 0, void 0, function () {
var useYarn, installCommand, _loop_1, packages_1, packages_1_1, _a, packageName, dev, e_1_1;
var e_1, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (packages.length === 0)
return [2 /*return*/];
(0, logger_1.status)("Installing dependencies...", "info");
return [4 /*yield*/, new Promise(function (resolve) {
(0, child_process_1.exec)("yarn --version", function (err) {
if (err)
resolve(false);
else
resolve(true);
});
})];
case 1:
useYarn = _c.sent();
installCommand = useYarn ? "yarn add" : "npm install";
_loop_1 = function (packageName, dev) {
return __generator(this, function (_d) {
switch (_d.label) {
case 0: return [4 /*yield*/, new Promise(function (resolve) {
(0, logger_1.status)("Installing ".concat(packageName, "..."), "info");
(0, child_process_1.exec)("".concat(installCommand, " ").concat(dev ? "-D " : "").concat(packageName), function (error) {
if (error) {
(0, logger_1.status)("Failed to install ".concat(packageName), "error");
}
resolve(undefined);
});
})];
case 1:
_d.sent();
return [2 /*return*/];
}
});
};
_c.label = 2;
case 2:
_c.trys.push([2, 7, 8, 9]);
packages_1 = __values(packages), packages_1_1 = packages_1.next();
_c.label = 3;
case 3:
if (!!packages_1_1.done) return [3 /*break*/, 6];
_a = packages_1_1.value, packageName = _a.packageName, dev = _a.dev;
return [5 /*yield**/, _loop_1(packageName, dev)];
case 4:
_c.sent();
_c.label = 5;
case 5:
packages_1_1 = packages_1.next();
return [3 /*break*/, 3];
case 6: return [3 /*break*/, 9];
case 7:
e_1_1 = _c.sent();
e_1 = { error: e_1_1 };
return [3 /*break*/, 9];
case 8:
try {
if (packages_1_1 && !packages_1_1.done && (_b = packages_1["return"])) _b.call(packages_1);
}
finally { if (e_1) throw e_1.error; }
return [7 /*endfinally*/];
case 9: return [2 /*return*/];
}
});
});
}
function updateEnvFile(options) {
return __awaiter(this, void 0, void 0, function () {
var envPath, envFilePath, envFileExists, env, _a, envJson, secretEnv, newEnv, apiEndpointEnv, graphqlEndpointEnv;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
(0, logger_1.status)("Creating/updating environment file...", "info");
envPath = getConfig_1.argv.envPath || ".env";
envFilePath = path_1["default"].join(process.cwd(), envPath);
envFileExists = fs_1["default"].existsSync(envFilePath);
if (!envFileExists) return [3 /*break*/, 2];
return [4 /*yield*/, fs_1["default"].promises.readFile(envFilePath, "utf8")];
case 1:
_a = _b.sent();
return [3 /*break*/, 3];
case 2:
_a = "";
_b.label = 3;
case 3:
env = _a;
envJson = envFileExists ? dotenv_1["default"].parse(env) : {};
secretEnv = getConfig_1.argv.secretEnv || getConfig_1.defaultSecretEnv;
newEnv = env;
if (!(envJson === null || envJson === void 0 ? void 0 : envJson[secretEnv]))
newEnv = env + "\n".concat(secretEnv, "=").concat(options.secret);
if (options.development) {
apiEndpointEnv = getConfig_1.argv.apiEndpointEnv || getConfig_1.defaultApiEndpointEnv;
graphqlEndpointEnv = getConfig_1.argv.graphqlEndpointEnv || getConfig_1.defaultGraphqlEndpointEnv;
if (!(envJson === null || envJson === void 0 ? void 0 : envJson[apiEndpointEnv]))
newEnv += "\n".concat(apiEndpointEnv, "=http://localhost:8443");
if (!(envJson === null || envJson === void 0 ? void 0 : envJson[graphqlEndpointEnv]))
newEnv += "\n".concat(graphqlEndpointEnv, "=http://localhost:8084");
}
// write environment file
return [4 /*yield*/, fs_1["default"].promises.writeFile(envFilePath, newEnv)];
case 4:
// write environment file
_b.sent();
return [2 /*return*/];
}
});
});
}
/** Create the resource directories */
function createResources(resources, concatSchema) {
if (concatSchema === void 0) { concatSchema = false; }
return __awaiter(this, void 0, void 0, function () {
var faunaDir, activeResources;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
// If no resources are selected, exit without doing anything.
if (resources.length === 0)
return [2 /*return*/];
(0, logger_1.status)("Creating placeholder resources...", "info");
faunaDir = path_1["default"].join(process.cwd(), "fauna");
if (!!fs_1["default"].existsSync(faunaDir)) return [3 /*break*/, 2];
return [4 /*yield*/, fs_1["default"].promises.mkdir(faunaDir)];
case 1:
_a.sent();
_a.label = 2;
case 2:
activeResources = resources
.filter(function (resource) { return !concatSchema ? resource !== "schema" : true; });
// Create resources.
return [4 /*yield*/, Promise.all(activeResources.map(function (resource) { return __awaiter(_this, void 0, void 0, function () {
var destDir, resourceDir, dirExists, resourcesFiles;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
destDir = path_1["default"].join(faunaDir, resourceDirs.get(resource) || resource);
resourceDir = path_1["default"].join(templatesDir, resource);
dirExists = fs_1["default"].existsSync(destDir);
if (!!dirExists) return [3 /*break*/, 2];
return [4 /*yield*/, fs_1["default"].promises.mkdir(destDir, { recursive: true })];
case 1:
_a.sent();
_a.label = 2;
case 2: return [4 /*yield*/, fs_1["default"].promises.readdir(resourceDir)];
case 3:
resourcesFiles = _a.sent();
return [4 /*yield*/, Promise.all(resourcesFiles.map(function (file) { return __awaiter(_this, void 0, void 0, function () {
var resourceTemplatePath;
return __generator(this, function (_a) {
resourceTemplatePath = path_1["default"].join(resourceDir, file);
return [2 /*return*/, fs_1["default"].promises.copyFile(resourceTemplatePath, path_1["default"].join(destDir, file))];
});
}); }))];
case 4:
_a.sent();
return [2 /*return*/];
}
});
}); }))
// Create schema file
];
case 3:
// Create resources.
_a.sent();
// Create schema file
if (!concatSchema && resources.includes("schema")) {
createSchema();
}
return [2 /*return*/];
}
});
});
}
/** Create a schema file */
function createSchema() {
return __awaiter(this, void 0, void 0, function () {
var schema, schemaTemplate;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
schema = path_1["default"].join(process.cwd(), "fauna", "schema.gql");
schemaTemplate = path_1["default"].join(templatesDir, "schema.gql");
return [4 /*yield*/, fs_1["default"].promises.copyFile(schemaTemplate, schema)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
}