Skip to content

Commit

Permalink
Add fauna schema pull --staged (#366)
Browse files Browse the repository at this point in the history
* Update schema.test to typescript

* Rename --stage to --staged

* Add fauna schema pull --staged
  • Loading branch information
macmv authored Sep 26, 2024
1 parent 1231c36 commit d86d3fb
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 34 deletions.
27 changes: 27 additions & 0 deletions src/commands/schema/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export default class PullSchemaCommand extends SchemaCommand {
"Delete .fsl files in the target directory that are not part of the database schema",
default: false,
}),
staged: Flags.boolean({
description: "Pulls staged schema instead of the active schema",
default: false,
}),
};

static description =
Expand All @@ -32,6 +36,29 @@ export default class PullSchemaCommand extends SchemaCommand {
if (filesjson.error) {
this.error(filesjson.error.message);
}

// Check if there's a staged schema, and require `--staged` if there is one.
const params = new URLSearchParams({
version: filesjson.version,
});
const statusres = await fetch(
new URL(`/schema/1/staged/status?${params}`, url),
{
method: "GET",
headers: { AUTHORIZATION: `Bearer ${secret}` },
}
);
const statusjson = await statusres.json();
if (statusjson.error) {
this.error(statusjson.error.message);
}

if (statusjson.status !== "none" && !this.flags?.staged) {
this.error("There is a staged schema change. Use --staged to pull it.");
} else if (statusjson.status === "none" && this.flags?.staged) {
this.error("There are no staged schema changes to pull.");
}

// Sort for consistent order. It's nice for tests.
const filenames = filesjson.files
.map((file: any) => file.filename)
Expand Down
8 changes: 4 additions & 4 deletions src/commands/schema/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default class PushSchemaCommand extends SchemaCommand {
description: "Push the change without a diff or schema version check",
default: false,
}),
stage: Flags.boolean({
staged: Flags.boolean({
description:
"Stages the schema change, instead of applying it immediately",
default: false,
Expand All @@ -22,7 +22,7 @@ export default class PushSchemaCommand extends SchemaCommand {
static examples = [
"$ fauna schema push",
"$ fauna schema push --dir schemas/myschema",
"$ fauna schema push --stage",
"$ fauna schema push --staged",
];

async run() {
Expand All @@ -33,7 +33,7 @@ export default class PushSchemaCommand extends SchemaCommand {
if (this.flags?.force) {
const params = new URLSearchParams({
force: "true", // Just push.
staged: this.flags?.stage ? "true" : "false",
staged: this.flags?.staged ? "true" : "false",
});

// This is how MDN says to do it for some reason.
Expand Down Expand Up @@ -88,7 +88,7 @@ export default class PushSchemaCommand extends SchemaCommand {
if (confirmed) {
const params = new URLSearchParams({
version: json.version,
staged: this.flags?.stage ? "true" : "false",
staged: this.flags?.staged ? "true" : "false",
});

const path = new URL(`/schema/1/update?${params}`, url);
Expand Down
151 changes: 121 additions & 30 deletions test/commands/schema.test.js → test/commands/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { expect } from "chai";
import { runCommand } from "@oclif/test";
import nock from "nock";
import sinon from "sinon";
const inquirer = require("@inquirer/prompts");
const fs = require("fs");
const path = require("path");
const { query: q } = require("faunadb");
const { withOpts, getEndpoint, matchFqlReq } = require("../helpers/utils.js");
const { disableColor } = require("../../src/lib/color");
import * as inquirer from "@inquirer/prompts";
import fs from "fs";
import path from "path";
import { query } from "faunadb";
import { withOpts, getEndpoint, matchFqlReq } from "../helpers/utils.js";
import { disableColor } from "../../src/lib/color";

const main = {
version: 0,
Expand Down Expand Up @@ -48,7 +48,7 @@ describe("fauna schema diff test", () => {
it("runs schema diff", async () => {
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.post("/schema/1/validate?force=true")
.reply(200, diff);
Expand All @@ -69,7 +69,7 @@ describe("fauna schema push test", () => {
it("runs schema push", async () => {
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.post("/schema/1/validate?force=true")
.reply(200, diff)
Expand All @@ -85,20 +85,20 @@ describe("fauna schema push test", () => {
stubConfirm.restore();
});

it("runs schema push --stage", async () => {
it("runs schema push --staged", async () => {
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.post("/schema/1/validate?force=true")
.reply(200, diff)
.post("/schema/1/update?version=0&stage=true")
.post("/schema/1/update?version=0&staged=true")
.reply(200, updated);

// Stubbing the confirmation to always return true
const stubConfirm = sinon.stub(inquirer, "confirm").resolves(true);
const { stdout } = await runCommand(
withOpts(["schema push", "--dir=test/testdata", "--stage"])
withOpts(["schema push", "--dir=test/testdata", "--staged"])
);
expect(stdout).to.contain(`${diff.diff}`);
// Restore the stub after the test
Expand All @@ -108,7 +108,7 @@ describe("fauna schema push test", () => {
it("runs schema status", async () => {
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/staged/status?diff=true")
.reply(200, {
Expand All @@ -127,7 +127,7 @@ describe("fauna schema push test", () => {
it("runs schema commit", async () => {
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/staged/status?diff=true")
.reply(200, {
Expand All @@ -151,7 +151,7 @@ describe("fauna schema push test", () => {
it("won't commit when schema isn't ready", async () => {
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/staged/status?diff=true")
.reply(200, {
Expand All @@ -166,15 +166,15 @@ describe("fauna schema push test", () => {
withOpts(["schema commit", "--dir=test/testdata"])
);
expect(stdout).to.contain(diff.diff);
expect(error.message).to.equal("Schema is not ready to be committed");
expect(error?.message).to.equal("Schema is not ready to be committed");
// Restore the stub after the test
stubConfirm.restore();
});

it("runs schema abandon", async () => {
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/staged/status?diff=true")
.reply(200, {
Expand All @@ -198,7 +198,7 @@ describe("fauna schema push test", () => {
it("will abandon even when schema isn't ready", async () => {
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/staged/status?diff=true")
.reply(200, {
Expand Down Expand Up @@ -226,7 +226,7 @@ const setup = () => {
try {
fs.unlinkSync(path.join(testdir, "functions.fsl"));
fs.rmSync(path.join(testdir, "roles"), { recursive: true, force: true });
} catch (err) {
} catch (err: any) {
// 2023 technology.
if (err.code === "ENOENT") {
// OK.
Expand All @@ -238,24 +238,26 @@ const setup = () => {
fs.writeFileSync(path.join(testdir, "extra.fsl"), "baaaaa");
};

for (const ddelete of [false, true]) {
describe(`fauna schema pull test (delete=${ddelete})`, () => {
let cmd = ["schema pull", `--dir=${testdir}`];
if (ddelete) {
cmd = ["schema pull", `--dir=${testdir}`, "--delete"];
}
setup();
it("runs schema pull", async () => {
describe(`fauna schema pull`, () => {
for (const ddelete of [false, true]) {
it(`runs schema pull (delete=${ddelete})`, async () => {
let cmd = ["schema pull", `--dir=${testdir}`];
if (ddelete) {
cmd = ["schema pull", `--dir=${testdir}`, "--delete"];
}
setup();
// Stubbing the confirmation to always return true
const stubConfirm = sinon.stub(inquirer, "confirm").resolves(true);

// Setting up the nock scope for API mocking
nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(q.Now()))
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/files")
.reply(200, pullfiles)
.get("/schema/1/staged/status?version=0")
.reply(200, { status: "none" })
.get("/schema/1/files/functions.fsl")
.reply(200, functions)
.get("/schema/1/files/main.fsl")
Expand Down Expand Up @@ -296,13 +298,102 @@ for (const ddelete of [false, true]) {
try {
fs.statSync(path.join(testdir, "extra.fsl"));
expect(0).to.equal(1); // Fail the test if file exists when it should not
} catch (err) {
} catch (err: any) {
expect(err.code).to.equal("ENOENT"); // Check that the error code is 'ENOENT'
}
}

// Clean up after test
stubConfirm.restore();
});
}

it(`requires --staged when there's a staged schema`, async () => {
const stubConfirm = sinon.stub(inquirer, "confirm").resolves(true);

nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/files")
.reply(200, pullfiles)
.get("/schema/1/staged/status?version=0")
.reply(200, { status: "ready" });

const { error } = await runCommand(
withOpts(["schema pull", `--dir=${testdir}`])
);
expect(error?.message).to.equal(
"There is a staged schema change. Use --staged to pull it."
);

stubConfirm.restore();
});

it(`disallows --staged when there's no staged schema`, async () => {
const stubConfirm = sinon.stub(inquirer, "confirm").resolves(true);

nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/files")
.reply(200, pullfiles)
.get("/schema/1/staged/status?version=0")
.reply(200, { status: "none" });

const { error } = await runCommand(
withOpts(["schema pull", `--dir=${testdir}`, `--staged`])
);
expect(error?.message).to.equal(
"There are no staged schema changes to pull."
);

stubConfirm.restore();
});
}

it(`runs schema pull --staged`, async () => {
const stubConfirm = sinon.stub(inquirer, "confirm").resolves(true);

nock(getEndpoint(), { allowUnmocked: false })
.persist()
.post("/", matchFqlReq(query.Now()))
.reply(200, new Date())
.get("/schema/1/files")
.reply(200, pullfiles)
.get("/schema/1/staged/status?version=0")
.reply(200, { status: "ready" })
.get("/schema/1/files/functions.fsl")
.reply(200, functions)
.get("/schema/1/files/main.fsl")
.reply(200, main)
.get("/schema/1/files/roles%2Fmyrole.fsl")
.reply(200, myrole);

// This should work as normal.
const { stdout } = await runCommand(
withOpts(["schema pull", `--dir=${testdir}`, `--staged`])
);
expect(stdout).to.contain("Pull makes the following changes:");
expect(
fs.readFileSync(path.join(testdir, "functions.fsl"), "utf8")
).to.equal(functions.content);
expect(fs.readFileSync(path.join(testdir, "main.fsl"), "utf8")).to.equal(
main.content
);
expect(
fs.readFileSync(path.join(testdir, "roles", "myrole.fsl"), "utf8")
).to.equal(myrole.content);
expect(fs.statSync(path.join(testdir, "no.fsl")).isDirectory()).to.equal(
true
);
expect(fs.statSync(path.join(testdir, "nofsl")).isDirectory()).to.equal(
true
);
expect(fs.statSync(path.join(testdir, "main.notfsl")).isFile()).to.equal(
true
);

stubConfirm.restore();
});
});

0 comments on commit d86d3fb

Please sign in to comment.