Skip to content

Commit

Permalink
catch schema commands up to main branch
Browse files Browse the repository at this point in the history
this implements changes in the flags for all schema commands as well
large changes in functionality for `schema diff` and `schema status`.
  • Loading branch information
echo-bravo-yahoo committed Oct 21, 2024
1 parent fe7b3a6 commit 0ea82fc
Show file tree
Hide file tree
Showing 12 changed files with 434 additions and 142 deletions.
106 changes: 89 additions & 17 deletions src/commands/schema/diff.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,119 @@ import { container } from "../../cli.mjs";
import { commonQueryOptions } from "../../lib/command-helpers.mjs";
import { reformatFSL } from "../../lib/schema.mjs";

/**
* @returns string[]
*/
function parseTarget(argv) {
if (!argv.active && !argv.staged) {
return ["staged", "local"];
}

if (argv.active && argv.staged) {
throw new Error("Cannot specify both --active and --staged");
}

if (argv.active) {
return ["active", "local"];
} else if (argv.staged) {
return ["active", "staged"];
} else {
throw new Error("Invalid target. Expected: active or staged");
}
}

async function doDiff(argv) {
const [source, target] = parseTarget(argv);
const diffKind = argv.text ? "textual" : "semantic";

const gatherFSL = container.resolve("gatherFSL");
const logger = container.resolve("logger");
const makeFaunaRequest = container.resolve("makeFaunaRequest");

const files = reformatFSL(await gatherFSL(argv.dir));

const params = new URLSearchParams({ force: "true" });
const params = new URLSearchParams({});
if (argv.color) params.set("color", "ansi");
params.set("staged", argv.staged);
if (target === "staged") params.set("diff", diffKind);

const response = await makeFaunaRequest({
const { version, status, diff } = await makeFaunaRequest({
baseUrl: argv.url,
path: new URL(`/schema/1/validate?${params}`, argv.url).href,
path: "/schema/1/staged/status",
params,
secret: argv.secret,
body: files,
method: "POST",
method: "GET",
});

const bold = argv.color ? chalk.bold : (str) => str;
const description = argv.staged ? "remote, staged" : "remote, active";
logger.stdout(
`Differences between the ${bold("local")} schema and the ${bold(
description,
)} schema:`,
);
logger.stdout(response.diff ? response.diff : "No schema differences");
if (target === "staged") {
logger.stdout(
`Differences from the ${chalk.bold("remote, active")} schema to the ${chalk.bold("remote, staged")} schema:`
);
if (status === "none") {
logger.stdout("There is no staged schema present.");
} else {
logger.stdout(diff ? diff : "No schema differences.");
}
} else {
const params = new URLSearchParams({ diff: diffKind, staged: String(source === "staged") });
if (argv.color) params.set("color", "ansi");
if (version) {
params.set("version", version);
} else {
params.set("force", "true");
}

const { diff } = await makeFaunaRequest({
baseUrl: argv.url,
path: "/schema/1/validate",
params,
secret: argv.secret,
body: files,
method: "POST",
});

if (status === "none") {
logger.stdout(
`Differences from the ${chalk.bold("remote")} schema to the ${chalk.bold("local")} schema:`
);
} else if (source === "active") {
logger.stdout(
`Differences from the ${chalk.bold("remote, active")} schema to the ${chalk.bold("local")} schema:`
);
} else {
logger.stdout(
`Differences from the ${chalk.bold("remote, staged")} schema to the ${chalk.bold("local")} schema:`
);
}
logger.stdout(diff ? diff : "No schema differences.");
}
}

function buildDiffCommand(yargs) {
return yargs
.options({
staged: {
description:
"Compare the local schema to the staged schema instead of the active schema.",
description: "Show the diff between the active and staged schema, instead of the local schema.",
default: false,
type: "boolean",
},
text: {
description: "Display the text diff instead of the semantic diff.",
default: false,
type: "boolean",
},
active: {
description: "Show the diff against the active schema instead of the staged schema.",
default: false,
type: "boolean",
},
...commonQueryOptions,
})
.example([["$0 schema diff"], ["$0 schema diff --dir schemas/myschema"]])
.example([
["$0 schema diff"],
["$0 schema diff --dir schemas/myschema"],
["$0 schema diff --staged"],
["$0 schema diff --active --text"],
])
.version(false)
.help("help", "show help");
}
Expand Down
37 changes: 20 additions & 17 deletions src/commands/schema/pull.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,38 @@

import { container } from "../../cli.mjs";
import { commonQueryOptions } from "../../lib/command-helpers.mjs";
import { makeFaunaRequest } from "../../lib/db.mjs";

async function doPull(argv) {
const logger = container.resolve("logger");
const gatherFSL = container.resolve("gatherFSL");
const confirm = container.resolve("confirm");
const getSchemaFiles = container.resolve("getSchemaFiles");
const getStagedSchemaStatus = container.resolve("getStagedSchemaStatus");

// fetch the list of remote FSL files
const filesResponse = await getSchemaFiles({
secret: argv.secret,
const filesResponse = await makeFaunaRequest({
baseUrl: argv.url,
path: "/schema/1/files",
method: "GET",
secret: argv.secret,
});

// check if there's a staged schema
const statusResponse = await getStagedSchemaStatus({
params: { version: filesResponse.version },
const statusResponse = await makeFaunaRequest({
baseUrl: argv.url,
secret: argv.secret,
});

// if there's a staged schema, require the --staged flag.
// getting unstaged FSL while staged FSL exists is not yet
path: "/schema/1/staged/status",
params: new URLSearchParams({ version: filesResponse.version }),
method: "GET",
secret: argv.secret
})

// if there's a staged schema, cannot use the --active flag.
// getting active FSL while staged FSL exists is not yet
// implemented at the service level.
if (statusResponse.status !== "none" && !argv.staged) {
if (statusResponse.status !== "none" && argv.active) {
throw new Error(
"There is a staged schema change. Use --staged to pull it.",
"There is a staged schema change. Remove the --active flag to pull it.",
);
} else if (statusResponse.status === "none" && argv.staged) {
} else if (statusResponse.status === "none" && !argv.active) {
throw new Error("There are no staged schema changes to pull.");
}

Expand Down Expand Up @@ -117,16 +120,16 @@ function buildPullCommand(yargs) {
type: "boolean",
default: false,
},
staged: {
description: "Pulls staged schema instead of the active schema",
active: {
description: "Pulls the active schema instead of the staged schema",
type: "boolean",
default: false,
},
...commonQueryOptions,
})
.example([
["$0 schema pull"],
["$0 schema pull --staged"],
["$0 schema pull --active"],
["$0 schema pull --delete"],
])
.version(false)
Expand Down
19 changes: 11 additions & 8 deletions src/commands/schema/push.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ async function doPush(argv) {
if (!argv.input) {
const params = new URLSearchParams({
force: "true",
staged: argv.staged ? "true" : "false",
staged: argv.active ? "false" : "true",
});

await makeFaunaRequest({
baseUrl: argv.url,
path: `/schema/1/update?${params}`,
path: "/schema/1/update",
params,
body: fsl,
secret: argv.secret,
method: "POST",
Expand All @@ -29,13 +30,14 @@ async function doPush(argv) {
// need to pass the last known schema version through.
const params = new URLSearchParams({
force: "true",
staged: argv.staged ? "true" : "false",
staged: argv.active ? "false" : "true",
});
if (argv.color) params.set("color", "ansi");

const response = await makeFaunaRequest({
baseUrl: argv.url,
path: `/schema/1/validate?${params}`,
path: "/schema/1/validate",
params,
body: fsl,
secret: argv.secret,
method: "POST",
Expand All @@ -58,12 +60,13 @@ async function doPush(argv) {
if (confirmed) {
const params = new URLSearchParams({
version: response.version,
staged: argv.staged ? "true" : "false",
staged: argv.active ? "false" : "true",
});

await makeFaunaRequest({
baseUrl: argv.url,
path: `/schema/1/update?${params}`,
path: "/schema/1/update",
params,
body: fsl,
secret: argv.secret,
method: "POST",
Expand All @@ -82,7 +85,7 @@ function buildPushCommand(yargs) {
default: true,
type: "boolean"
},
staged: {
active: {
description:
"Stages the schema change instead of applying it immediately",
type: "boolean",
Expand All @@ -93,7 +96,7 @@ function buildPushCommand(yargs) {
.example([
["$0 schema push"],
["$0 schema push --dir schemas/myschema"],
["$0 schema push --staged"],
["$0 schema push --active"],
])
.version(false)
.help("help", "show help");
Expand Down
45 changes: 41 additions & 4 deletions src/commands/schema/status.mjs
Original file line number Diff line number Diff line change
@@ -1,23 +1,60 @@
//@ts-check

import chalk from "chalk";

import { container } from "../../cli.mjs";
import { commonQueryOptions } from "../../lib/command-helpers.mjs";
import { reformatFSL } from "../../lib/schema.mjs";

async function doStatus(argv) {
const logger = container.resolve("logger");
const makeFaunaRequest = container.resolve("makeFaunaRequest");

const params = new URLSearchParams({ diff: "true" });
let params = new URLSearchParams({ diff: "summary" });
if (argv.color) params.set("color", "ansi");
const gatherFSL = container.resolve("gatherFSL");
const fsl = reformatFSL(await gatherFSL(argv.dir));

const response = await makeFaunaRequest({
const statusResponse = await makeFaunaRequest({
baseUrl: argv.url,
path: `/schema/1/staged/status?${params}`,
path: "/schema/1/staged/status",
params,
secret: argv.secret,
method: "GET",
});
// console.log('statusResponse', statusResponse)

params = new URLSearchParams({ diff: "summary", staged: "true", version: statusResponse.version })
const validationResponse = await makeFaunaRequest({
baseUrl: argv.url,
path: "/schema/1/validate",
params,
secret: argv.secret,
method: "POST",
body: fsl
});
// console.log('validationResponse', validationResponse)

logger.stdout(`Staged changes: ${chalk.bold(statusResponse.status)}`);
if (statusResponse.pending_summary !== "") {
logger.stdout(statusResponse.pending_summary);
}
if (statusResponse.diff) {
logger.stdout("Staged changes:\n");
logger.stdout(statusResponse.diff.split("\n").join("\n "));
}

logger.stdout(response.diff);
if (validationResponse.error) {
logger.stdout(`Local changes:`);
throw new Error(validationResponse.error.message);
} else if (validationResponse.diff === "") {
logger.stdout(`Local changes: ${chalk.bold("none")}\n`);
} else {
logger.stdout(`Local changes:\n`);
logger.stdout(" " + validationResponse.diff.split("\n").join("\n "));
logger.stdout("(use `fauna schema diff` to display local changes)");
logger.stdout("(use `fauna schema push` to stage local changes)");
}
}

function buildStatusCommand(yargs) {
Expand Down
6 changes: 0 additions & 6 deletions src/config/setup-container.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ import {
gatherFSL,
gatherRelativeFSLFilePaths,
getAllSchemaFileContents,
getStagedSchemaStatus,
getSchemaFile,
getSchemaFiles,
deleteUnusedSchemaFiles,
writeSchemaFiles,
} from "../lib/schema.mjs";
Expand Down Expand Up @@ -81,11 +78,8 @@ export const injectables = {
// feature-specific lib (homemade utilities)
gatherFSL: awilix.asValue(gatherFSL),
gatherRelativeFSLFilePaths: awilix.asValue(gatherRelativeFSLFilePaths),
getSchemaFile: awilix.asValue(getSchemaFile),
getSchemaFiles: awilix.asValue(getSchemaFiles),
writeSchemaFiles: awilix.asValue(writeSchemaFiles),
getAllSchemaFileContents: awilix.asValue(getAllSchemaFileContents),
getStagedSchemaStatus: awilix.asValue(getStagedSchemaStatus),
deleteUnusedSchemaFiles: awilix.asValue(deleteUnusedSchemaFiles),
};

Expand Down
5 changes: 3 additions & 2 deletions src/lib/db.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { container } from "../cli.mjs";
* @property {string} secret - The secret to include in the AUTHORIZATION header of the request.
* @property {string} baseUrl - The base URL from the scheme up through the top level domain and optional port; defaults to "https://db.fauna.com:443".
* @property {string} path - The path part of the URL. Added to the baseUrl and params to build the full URL.
* @property {Record<string, string>} [params] - The parameters (and their values) to set in the query string.
* @property {URLSearchParams} [params] - The parameters (and their values) to set in the query string.
* @property {method} method - The HTTP method to use when making the request.
* @property {object} [body] - The body to include in the request.
* @property {boolean} [shouldThrow=true] - Whether or not to throw if the network request succeeds but is not a 2XX. If this is set to false, makeFaunaRequest will return the error instead of throwing.
Expand All @@ -30,7 +30,8 @@ export async function makeFaunaRequest({
shouldThrow = true,
}) {
const fetch = container.resolve("fetch");
const paramsString = params ? `?${new URLSearchParams(params)}` : "";
if (params && params.sort) params.sort()
const paramsString = params && params.size ? `?${params.toString()}` : "";
let fullUrl;

try {
Expand Down
Loading

0 comments on commit 0ea82fc

Please sign in to comment.