Skip to content

Commit

Permalink
FE-6254 Setting schema on 'fauna local --database Foo' (#533)
Browse files Browse the repository at this point in the history
* FE-6254 Boilerplate for setting schema on 'fauna local --database Foo'

* Refactor location of push logic to enable code sharing

* Push schema support for 'fauna local'

* Happy path force tests

* Test fixes

* Half borken tests

* Half borken tests

* Fix interactive push tests on local

* Move schema back into commands dir

* Invert the --active flag for fauna local usage

---------

Co-authored-by: E. Cooper <[email protected]>
Co-authored-by: Matthew Wilde <[email protected]>
  • Loading branch information
3 people authored Dec 17, 2024
1 parent 0e04830 commit feca576
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 39 deletions.
52 changes: 52 additions & 0 deletions src/commands/local.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import chalk from "chalk";
import { AbortError } from "fauna";

import { container } from "../cli.mjs";
import { pushSchema } from "../commands/schema/push.mjs";
import { ensureContainerRunning } from "../lib/docker-containers.mjs";
import { CommandError, ValidationError } from "../lib/errors.mjs";
import { colorize, Format } from "../lib/formatting/colorize.mjs";
Expand All @@ -28,6 +29,34 @@ async function startLocal(argv) {
if (argv.database) {
await createDatabase(argv);
}
if (argv.directory) {
await createDatabaseSchema(argv);
}
}

async function createDatabaseSchema(argv) {
const logger = container.resolve("logger");
logger.stderr(
colorize(
`[CreateDatabaseSchema] Creating schema for database '${argv.database}' from directory '${argv.directory}'...`,
{
format: Format.LOG,
color: argv.color,
},
),
);
// hack to let us push schema to the local database
argv.secret = `secret:${argv.database}:admin`;
await pushSchema(argv);
logger.stderr(
colorize(
`[CreateDatabaseSchema] Schema for database '${argv.database}' created from directory '${argv.directory}'.`,
{
format: Format.LOG,
color: argv.color,
},
),
);
}

async function createDatabase(argv) {
Expand Down Expand Up @@ -152,6 +181,24 @@ function buildLocalCommand(yargs) {
description:
"User-defined priority for the database. Valid only if --database is set.",
},
"project-directory": {
type: "string",
alias: ["dir", "directory"],
description:
"Path to a local directory containing `.fsl` files for the database. Valid only if --database is set.",
},
active: {
description:
"Immediately applies the local schema to the database's active schema, skipping staging the schema. To disable this, use `--no-active` or `--active=false`.",
type: "boolean",
default: true,
},
input: {
description:
"Prompt for schema input, such as confirmation. To disable prompts, use `--no-input` or `--input=false`. Disabled prompts are useful for scripts, CI/CD, and automation workflows.",
default: true,
type: "boolean",
},
})
.check((argv) => {

Check warning on line 203 in src/commands/local.mjs

View workflow job for this annotation

GitHub Actions / lint

Arrow function has a complexity of 11. Maximum allowed is 10
if (argv.maxAttempts < 1) {
Expand All @@ -177,6 +224,11 @@ function buildLocalCommand(yargs) {
"--priority can only be set if --database is set.",
);
}
if (argv.directory && !argv.database) {
throw new ValidationError(
"--directory,--dir can only be set if --database is set.",
);
}
return true;
});
}
Expand Down
9 changes: 7 additions & 2 deletions src/commands/schema/push.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { getSecret } from "../../lib/fauna-client.mjs";
import { reformatFSL } from "../../lib/schema.mjs";
import { localSchemaOptions } from "./schema.mjs";

async function doPush(argv) {
/**
* Pushes a schema (FSL) based on argv.
* @param {import("yargs").Argv & {dir: string, active: boolean, input: boolean}} argv
*/
export async function pushSchema(argv) {
const logger = container.resolve("logger");
const makeFaunaRequest = container.resolve("makeFaunaRequest");
const gatherFSL = container.resolve("gatherFSL");
Expand Down Expand Up @@ -66,6 +70,7 @@ async function doPush(argv) {
? "Stage the file contents anyway?"
: "Push the file contents anyway?";
}

const confirm = container.resolve("confirm");
const confirmed = await confirm({
message,
Expand Down Expand Up @@ -133,5 +138,5 @@ export default {
command: "push",
description: "Push local .fsl schema files to Fauna.",
builder: buildPushCommand,
handler: doPush,
handler: pushSchema,
};
9 changes: 5 additions & 4 deletions src/lib/auth/credentials.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ import { asValue, Lifetime } from "awilix";
import { container } from "../../cli.mjs";
import { ValidationError } from "../errors.mjs";
import { FaunaAccountClient } from "../fauna-account-client.mjs";
import { isLocal } from "../middleware.mjs";
import { AccountKeys } from "./accountKeys.mjs";
import { DatabaseKeys } from "./databaseKeys.mjs";

const validateCredentialArgs = (argv) => {
const logger = container.resolve("logger");
const illegalArgCombos = [
["accountKey", "secret", "local"],
["secret", "database", "local"],
["secret", "role", "local"],
["accountKey", "secret", isLocal],
["secret", "database", isLocal],
["secret", "role", isLocal],
];
for (const [first, second, conditional] of illegalArgCombos) {
if (argv[first] && argv[second] && !argv[conditional]) {
if (argv[first] && argv[second] && !conditional(argv)) {
throw new ValidationError(
`Cannot use both the '--${first}' and '--${second}' options together. Please specify only one.`,
);
Expand Down
15 changes: 12 additions & 3 deletions src/lib/middleware.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { container } from "../cli.mjs";
import { fixPath } from "../lib/file-util.mjs";
import { redactedStringify } from "./formatting/redact.mjs";

const LOCAL_URL = "http://localhost:8443";
const LOCAL_URL = "http://0.0.0.0:8443";
const LOCAL_SECRET = "secret";
const DEFAULT_URL = "https://db.fauna.com";

Expand Down Expand Up @@ -79,6 +79,15 @@ export function applyLocalArg(argv) {
applyLocalToSecret(argv);
}

/**
* @param {import('yargs').Arguments} argv
* @returns {boolean} true if this command acts on a local
* container, false otherwise.
*/
export function isLocal(argv) {
return argv.local || argv._[0] === "local";
}

/**
* Mutates argv.url appropriately for local Fauna usage
* (i.e. local container usage). If --local is provided
Expand All @@ -89,7 +98,7 @@ export function applyLocalArg(argv) {
function applyLocalToUrl(argv) {
const logger = container.resolve("logger");
if (!argv.url) {
if (argv.local) {
if (isLocal(argv)) {
argv.url = LOCAL_URL;
logger.debug(
`Set url to '${LOCAL_URL}' as --local was given and --url was not`,
Expand Down Expand Up @@ -120,7 +129,7 @@ function applyLocalToUrl(argv) {
*/
function applyLocalToSecret(argv) {
const logger = container.resolve("logger");
if (!argv.secret && argv.local) {
if (!argv.secret && isLocal(argv)) {
if (argv.role && argv.database) {
argv.secret = `${LOCAL_SECRET}:${argv.database}:${argv.role}`;
} else if (argv.role) {
Expand Down
8 changes: 4 additions & 4 deletions test/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,13 @@ describe("configuration file", function () {
});
});

it("--local arg sets the argv.url to http://localhost:8443 if no --url is given", async function () {
it("--local arg sets the argv.url to http://0.0.0.0:8443 if no --url is given", async function () {
fs.readdirSync.withArgs(process.cwd()).returns([]);
await runArgvTest({
cmd: `argv --secret "no-config" --local`,
argvMatcher: sinon.match({
secret: "no-config",
url: "http://localhost:8443",
url: "http://0.0.0.0:8443",
}),
});
});
Expand All @@ -205,7 +205,7 @@ describe("configuration file", function () {
cmd: `argv --local`,
argvMatcher: sinon.match({
secret: "secret",
url: "http://localhost:8443",
url: "http://0.0.0.0:8443",
}),
});
});
Expand All @@ -216,7 +216,7 @@ describe("configuration file", function () {
cmd: `argv --local --secret "sauce"`,
argvMatcher: sinon.match({
secret: "sauce",
url: "http://localhost:8443",
url: "http://0.0.0.0:8443",
}),
});
});
Expand Down
6 changes: 3 additions & 3 deletions test/database/list.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,22 @@ describe("database list", () => {
[
{
args: "--local",
expected: { secret: "secret", url: "http://localhost:8443" },
expected: { secret: "secret", url: "http://0.0.0.0:8443" },
},
{
args: "--local --url http://yo_dog:8443",
expected: { secret: "secret", url: "http://yo_dog:8443" },
},
{
args: "--local --secret taco",
expected: { secret: "taco", url: "http://localhost:8443" },
expected: { secret: "taco", url: "http://0.0.0.0:8443" },
},
{
args: "--local --pageSize 10",
expected: {
secret: "secret",
pageSize: 10,
url: "http://localhost:8443",
url: "http://0.0.0.0:8443",
},
},
].forEach(({ args, expected }) => {
Expand Down
6 changes: 3 additions & 3 deletions test/lib/middleware.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ describe("middlewares", function () {
setupTestContainer();
});

it("should set url to localhost:8443 when --local is true and no url provided", function () {
it("should set url to 0.0.0.0:8443 when --local is true and no url provided", function () {
const argv = { ...baseArgv, local: true };
applyLocalArg(argv);
expect(argv.url).to.equal("http://localhost:8443");
expect(argv.url).to.equal("http://0.0.0.0:8443");
expect(argv.secret).to.equal("secret");
});

Expand All @@ -38,7 +38,7 @@ describe("middlewares", function () {
it("should not modify secret if already provided", function () {
const argv = { ...baseArgv, local: true, secret: "custom-secret" };
applyLocalArg(argv);
expect(argv.url).to.equal("http://localhost:8443");
expect(argv.url).to.equal("http://0.0.0.0:8443");
expect(argv.secret).to.equal("custom-secret");
});

Expand Down
Loading

0 comments on commit feca576

Please sign in to comment.