From 8ded068fcaedee7b34bc6dc25ba8c87e893ad75b Mon Sep 17 00:00:00 2001 From: Neil Macneale V Date: Wed, 27 Sep 2023 13:52:18 -0700 Subject: [PATCH] Use project config in schema commands (#253) * Add gitignore for test output * Use project config path as a fallacbk from --dir --- src/commands/schema/diff.js | 11 ++----- src/commands/schema/pull.js | 16 +++------ src/commands/schema/push.js | 10 ++---- src/commands/shell.js | 4 ++- src/lib/config/index.ts | 8 +++++ src/lib/fauna-command.js | 15 +++++++-- src/lib/schema-command.js | 49 +++++++++++++++++++++------- test/lib/fauna-import-writer.test.js | 3 +- test/testdata/.gitignore | 1 + 9 files changed, 72 insertions(+), 45 deletions(-) create mode 100644 test/testdata/.gitignore diff --git a/src/commands/schema/diff.js b/src/commands/schema/diff.js index 12096c54..62f0802d 100644 --- a/src/commands/schema/diff.js +++ b/src/commands/schema/diff.js @@ -1,21 +1,14 @@ const SchemaCommand = require("../../lib/schema-command.js").default; const fetch = require("node-fetch"); -const { Flags } = require("@oclif/core"); class DiffSchemaCommand extends SchemaCommand { static flags = { ...SchemaCommand.flags, - // NB: Required as a flag because it will become optional eventually, - // once project configuration is implemented. - dir: Flags.string({ - required: true, - description: "The directory of .fsl files to push", - }), }; async run() { - const fps = this.gather(this.flags.dir); - const files = this.read(this.flags.dir, fps); + const fps = this.gather(); + const files = this.read(fps); try { const { urlbase, secret } = await this.fetchsetup(); const res = await fetch(`${urlbase}/schema/1/validate?force=true`, { diff --git a/src/commands/schema/pull.js b/src/commands/schema/pull.js index e499ce24..dc3e4424 100644 --- a/src/commands/schema/pull.js +++ b/src/commands/schema/pull.js @@ -12,12 +12,6 @@ class PullSchemaCommand extends SchemaCommand { "Delete .fsl files in the target directory that are not part of the database schema", default: false, }), - // NB: Required as a flag because it will become optional eventually, - // once project configuration is implemented. - dir: Flags.string({ - required: true, - description: "The target directory", - }), }; async confirm() { @@ -37,8 +31,6 @@ class PullSchemaCommand extends SchemaCommand { async run() { const { urlbase, secret } = await this.fetchsetup(); - const dir = this.flags.dir; - try { // Gather remote schema files to download. const filesres = await fetch(`${urlbase}/schema/1/files`, { @@ -56,7 +48,7 @@ class PullSchemaCommand extends SchemaCommand { .sort(); // Gather local .fsl files to overwrite or delete. - const existing = this.gather(dir); + const existing = this.gather(); // Summarize file changes. const adds = []; @@ -92,7 +84,7 @@ class PullSchemaCommand extends SchemaCommand { if (this.flags.delete) { // Delete extra .fsl files. for (const deleteme of deletes) { - fs.unlinkSync(path.join(dir, deleteme)); + fs.unlinkSync(path.join(this.dir, deleteme)); } } @@ -106,7 +98,7 @@ class PullSchemaCommand extends SchemaCommand { if (filejson.error) { this.error(filejson.error.message); } - const fp = path.join(dir, filename); + const fp = path.join(this.dir, filename); fs.mkdirSync(path.dirname(fp), { recursive: true }); fs.writeFileSync(fp, filejson.content); } @@ -122,6 +114,6 @@ class PullSchemaCommand extends SchemaCommand { PullSchemaCommand.description = "Pull a database schema's .fsl files into a directory"; -PullSchemaCommand.examples = ["$ fauna schema pull --dir schemas/myschema"]; +PullSchemaCommand.examples = ["$ fauna schema pull"]; module.exports = PullSchemaCommand; diff --git a/src/commands/schema/push.js b/src/commands/schema/push.js index 0ea5e7d3..b766ddcb 100644 --- a/src/commands/schema/push.js +++ b/src/commands/schema/push.js @@ -9,12 +9,6 @@ class PushSchemaCommand extends SchemaCommand { description: "Push the change without a diff or schema version check", default: false, }), - // NB: Required as a flag because it will become optional eventually, - // once project configuration is implemented. - dir: Flags.string({ - required: true, - description: "The directory of .fsl files to push", - }), }; async confirm() { @@ -32,8 +26,8 @@ class PushSchemaCommand extends SchemaCommand { } async run() { - const fps = this.gather(this.flags.dir); - const files = this.read(this.flags.dir, fps); + const fps = this.gather(); + const files = this.read(fps); try { const { urlbase, secret } = await this.fetchsetup(); if (this.flags.force) { diff --git a/src/commands/shell.js b/src/commands/shell.js index 8af852c1..47ab82e4 100644 --- a/src/commands/shell.js +++ b/src/commands/shell.js @@ -67,7 +67,9 @@ class ShellCommand extends EvalCommand { // we don't want to allow people to call some of the default commands // from the node repl const entries = Object.entries(this.repl.commands); - this.repl.commands = Object.fromEntries(entries.filter(([k, _]) => !["load", "save"].includes(k))); + this.repl.commands = Object.fromEntries( + entries.filter(([k, _]) => !["load", "save"].includes(k)) + ); this.commands.forEach(({ cmd, ...cmdOptions }) => this.repl.defineCommand(cmd, cmdOptions) diff --git a/src/lib/config/index.ts b/src/lib/config/index.ts index 589e3746..bac6fd7c 100644 --- a/src/lib/config/index.ts +++ b/src/lib/config/index.ts @@ -102,6 +102,7 @@ export class Config { export type ShellOpts = { flags?: { [key: string]: any }; rootConfig?: { [key: string]: any }; + projectPath?: string; projectConfig?: { [key: string]: any }; }; @@ -124,6 +125,8 @@ export class ShellConfig { // // If this is unset, `validate` will fail. endpoint: Endpoint | undefined; + // The path to the project config. + projectPath: string | undefined; static read(flags: any) { const rootConfig = ini.parse(readFileOpt(getRootConfigPath())); @@ -135,6 +138,10 @@ export class ShellConfig { return new ShellConfig({ flags, rootConfig, + projectPath: + projectConfigPath !== undefined + ? path.dirname(projectConfigPath) + : undefined, projectConfig, }); } @@ -145,6 +152,7 @@ export class ShellConfig { this.rootConfig = new RootConfig( new Config("config key", opts.rootConfig ?? {}) ); + this.projectPath = opts.projectPath; this.projectConfig = opts.projectConfig ? new ProjectConfig(new Config("config key", opts.projectConfig)) : undefined; diff --git a/src/lib/fauna-command.js b/src/lib/fauna-command.js index 372d9774..ce2e7602 100644 --- a/src/lib/fauna-command.js +++ b/src/lib/fauna-command.js @@ -54,7 +54,10 @@ class FaunaCommand extends Command { async withClient(f, dbScope, role) { let connectionOptions; try { - connectionOptions = this.shellConfig.lookupEndpoint({ scope: dbScope, role }); + connectionOptions = this.shellConfig.lookupEndpoint({ + scope: dbScope, + role, + }); const { hostname, port, protocol } = new URL(connectionOptions.url); @@ -95,7 +98,10 @@ class FaunaCommand extends Command { // construct v4 client let connectionOptions; try { - connectionOptions = this.shellConfig.lookupEndpoint({ scope: dbScope, role }); + connectionOptions = this.shellConfig.lookupEndpoint({ + scope: dbScope, + role, + }); const { hostname, port, protocol } = new URL(connectionOptions.url); @@ -130,7 +136,10 @@ class FaunaCommand extends Command { // construct v10 client let connectionOptions; try { - connectionOptions = this.shellConfig.lookupEndpoint({ scope: dbScope, role }); + connectionOptions = this.shellConfig.lookupEndpoint({ + scope: dbScope, + role, + }); const client = new FaunaClient( connectionOptions.url, connectionOptions.secret, diff --git a/src/lib/schema-command.js b/src/lib/schema-command.js index 3526fa7e..27a0a499 100644 --- a/src/lib/schema-command.js +++ b/src/lib/schema-command.js @@ -1,13 +1,21 @@ import FaunaCommand from "./fauna-command"; -import { readFileSync, readdirSync, statSync } from "fs"; -import { join } from "path"; +import * as fs from "fs"; +import * as path from "path"; import FormData from "form-data"; +import { Flags } from "@oclif/core"; class SchemaCommand extends FaunaCommand { static flags = (() => { // Remove flags that don't make sense. const { graphqlHost, graphqlPort, ...rest } = FaunaCommand.flags; - return rest; + return { + dir: Flags.string({ + description: + "The directory of .fsl files to push. Defaults to the directory of `.fauna-project`", + required: false, + }), + ...rest, + }; })(); async fetchsetup() { @@ -21,6 +29,25 @@ class SchemaCommand extends FaunaCommand { }; } + /** + * @type {string} + */ + dir; + + async init() { + await super.init(); + + if (this.flags.dir !== undefined) { + this.dir = this.flags.dir; + } else if (this.shellConfig.projectPath !== undefined) { + this.dir = this.shellConfig.projectPath; + } else { + this.error( + "No project found. Create a project with `fauna project init`." + ); + } + } + // Helper to construct form data for a collection of files, as // returned by `gather`. body(files) { @@ -34,13 +61,13 @@ class SchemaCommand extends FaunaCommand { // Reads the files using their relative-to-`basedir` paths and returns their // contents paired with the relative path. // Fails if the total size of the files is too large. - read(basedir, relpaths) { + read(relpaths) { const FILESIZE_LIMIT_BYTES = 32 * 1024 * 1024; const curr = []; var totalsize = 0; for (const relp of relpaths) { - const fp = join(basedir, relp); - const content = readFileSync(fp); + const fp = path.join(this.dir, relp); + const content = fs.readFileSync(fp); totalsize += content.length; if (totalsize > FILESIZE_LIMIT_BYTES) { this.error( @@ -55,15 +82,15 @@ class SchemaCommand extends FaunaCommand { // Gathers all FSL files in the directory rooted at `basedir` and returns a // list of relative paths. // Fails if there are too many files. - gather(basedir) { + gather() { const FILE_LIMIT = 256; const go = (rel, curr) => { - const names = readdirSync(join(basedir, rel)); + const names = fs.readdirSync(path.join(this.dir, rel)); const subdirs = []; for (const n of names) { - const fp = join(basedir, rel, n); - const relp = join(rel, n); - const isDir = statSync(fp).isDirectory(); + const fp = path.join(this.dir, rel, n); + const relp = path.join(rel, n); + const isDir = fs.statSync(fp).isDirectory(); if (n.endsWith(".fsl") && !isDir) { curr.push(relp); } diff --git a/test/lib/fauna-import-writer.test.js b/test/lib/fauna-import-writer.test.js index 504fcb6c..63aa4632 100644 --- a/test/lib/fauna-import-writer.test.js +++ b/test/lib/fauna-import-writer.test.js @@ -1,5 +1,6 @@ const expect = require("expect"); -const getFaunaImportWriter = require("../../src/lib/fauna-import-writer").default; +const getFaunaImportWriter = + require("../../src/lib/fauna-import-writer").default; const jestMock = require("jest-mock"); const sizeof = require("object-sizeof"); const { UnavailableError, FaunaHTTPError } = require("faunadb").errors; diff --git a/test/testdata/.gitignore b/test/testdata/.gitignore new file mode 100644 index 00000000..755189b0 --- /dev/null +++ b/test/testdata/.gitignore @@ -0,0 +1 @@ +roles/