Skip to content

Commit

Permalink
Support users embedding fql snippets in objects and arrays. (#214)
Browse files Browse the repository at this point in the history
Co-authored-by: Cleve Stuart <[email protected]>
  • Loading branch information
ptpaterson and cleve-fauna authored Sep 29, 2023
1 parent f48b07c commit 5e6d7d8
Show file tree
Hide file tree
Showing 10 changed files with 609 additions and 204 deletions.
66 changes: 66 additions & 0 deletions __tests__/integration/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,4 +499,70 @@ describe("query can encode / decode QueryValue correctly", () => {
}
}
});

it("symbol arguments throw a TypeError", async () => {
expect.assertions(2);
// whack in a symbol
// @ts-expect-error Type 'symbol' is not assignable to type 'QueryValue'
let symbolValue: QueryValue = Symbol("foo");
try {
await client.query(fql`{ foo: ${symbolValue} }`);
} catch (e) {
if (e instanceof TypeError) {
expect(e.name).toBe("TypeError");
expect(e.message).toBe(
"Passing symbol as a QueryValue is not supported"
);
}
}
});

it("function arguments throw a TypeError", async () => {
expect.assertions(2);
// whack in a function
let fnValue: QueryValue = () => {};
try {
await client.query(fql`{ foo: ${fnValue} }`);
} catch (e) {
if (e instanceof TypeError) {
expect(e.name).toBe("TypeError");
expect(e.message).toBe(
"Passing function as a QueryValue is not supported"
);
}
}
});

it("symbol arguments throw a TypeError in arguments", async () => {
expect.assertions(2);
// whack in a symbol
// @ts-expect-error Type 'symbol' is not assignable to type 'QueryValue'
let symbolValue: QueryValue = Symbol("foo");
try {
await client.query(fql`foo`, { arguments: { foo: symbolValue } });
} catch (e: any) {
if (e instanceof ClientError && e.cause instanceof TypeError) {
expect(e.cause.name).toBe("TypeError");
expect(e.cause.message).toBe(
"Passing symbol as a QueryValue is not supported"
);
}
}
});

it("function arguments throw a TypeError in arguments", async () => {
expect.assertions(2);
// whack in a function
let fnValue: QueryValue = () => {};
try {
await client.query(fql`foo`, { arguments: { foo: fnValue } });
} catch (e: any) {
if (e instanceof ClientError && e.cause instanceof TypeError) {
expect(e.cause.name).toBe("TypeError");
expect(e.cause.message).toBe(
"Passing function as a QueryValue is not supported"
);
}
}
});
});
58 changes: 58 additions & 0 deletions __tests__/integration/template-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,64 @@ describe("query using template format", () => {
expect(response.data).toBe(true);
});

it("succeeds with deep nested expressions - example 2", async () => {
const str = "foo";
const otherStr = "bar";
const num = 6;
const otherNum = 3;
const deepFirst = fql`(${str} + ${otherStr})`;
const deeperBuilder = fql`(${num} + 3)`;
const innerQuery = fql`(${deeperBuilder} + ${otherNum})`;
const queryBuilder = fql`${deepFirst}.length + ${innerQuery}`;
const response = await client.query(queryBuilder);
expect(response.data).toBe(18);
});

it("succeeds with expressions nested within objects", async () => {
const arg = {
a: fql`1`,
b: fql`2`,
};
const queryBuilder = fql`${arg}`;
const response = await client.query(queryBuilder);
expect(response.data).toStrictEqual({ a: 1, b: 2 });
});

it("succeeds with expressions nested within arrays", async () => {
const arg = [fql`1`, fql`2`];
const queryBuilder = fql`${arg}`;
const response = await client.query(queryBuilder);
expect(response.data).toEqual([1, 2]);
});

it("succeeds with expressions nested within arrays and objects combined", async () => {
const arg = [
[fql`1`],
{
a: fql`1`,
b: fql`2`,
},
];
const queryBuilder = fql`${arg}`;
const response = await client.query(queryBuilder);
expect(response.data).toEqual([[1], { a: 1, b: 2 }]);
});

it("succeeds with multiple layers of nesting of arrays and objects", async () => {
const other = { a: fql`3`, b: fql`4` };
const arg = [
[fql`1 + ${fql`2`}`],
{
a: fql`1`,
b: fql`2`,
c: other,
},
];
const queryBuilder = fql`${arg}`;
const response = await client.query(queryBuilder);
expect(response.data).toEqual([[3], { a: 1, b: 2, c: { a: 3, b: 4 } }]);
});

it("succeeds with FQL string interpolation", async () => {
const codeName = "Alice";
const queryBuilder = fql`
Expand Down
79 changes: 33 additions & 46 deletions __tests__/unit/query-builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe("fql method producing Querys", () => {
const queryBuilder = fql`'foo'.length`;
const queryRequest = queryBuilder.toQuery();
expect(queryRequest.query).toEqual({ fql: ["'foo'.length"] });
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with a string variable", () => {
Expand All @@ -15,7 +15,7 @@ describe("fql method producing Querys", () => {
expect(queryRequest.query).toEqual({
fql: [{ value: "foo" }, ".length"],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with a number variable", () => {
Expand All @@ -25,7 +25,7 @@ describe("fql method producing Querys", () => {
expect(queryRequest.query).toEqual({
fql: ["'foo'.length == ", { value: { "@int": "8" } }],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with a boolean variable", () => {
Expand All @@ -35,7 +35,7 @@ describe("fql method producing Querys", () => {
expect(queryRequest.query).toEqual({
fql: ["val.enabled == ", { value: true }],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with a null variable", () => {
Expand All @@ -44,27 +44,39 @@ describe("fql method producing Querys", () => {
expect(queryRequest.query).toEqual({
fql: ["value: ", { value: null }],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with an object variable", () => {
const obj = { foo: "bar", bar: "baz" };
const queryBuilder = fql`value: ${obj}`;
const queryRequest = queryBuilder.toQuery();
expect(queryRequest.query).toEqual({
fql: ["value: ", { value: { bar: "baz", foo: "bar" } }],
fql: [
"value: ",
{ object: { bar: { value: "baz" }, foo: { value: "bar" } } },
],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with an object variable having a toQuery property", () => {
const obj = { foo: "bar", bar: "baz", toQuery: "hehe" };
const queryBuilder = fql`value: ${obj}`;
const queryRequest = queryBuilder.toQuery();
expect(queryRequest.query).toEqual({
fql: ["value: ", { value: { bar: "baz", foo: "bar", toQuery: "hehe" } }],
fql: [
"value: ",
{
object: {
bar: { value: "baz" },
foo: { value: "bar" },
toQuery: { value: "hehe" },
},
},
],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with an array variable", () => {
Expand All @@ -74,10 +86,16 @@ describe("fql method producing Querys", () => {
expect(queryRequest.query).toEqual({
fql: [
"value: ",
{ value: [{ "@int": "1" }, { "@int": "2" }, { "@int": "3" }] },
{
array: [
{ value: { "@int": "1" } },
{ value: { "@int": "2" } },
{ value: { "@int": "3" } },
],
},
],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with multiple variables", () => {
Expand All @@ -88,7 +106,7 @@ describe("fql method producing Querys", () => {
expect(queryRequest.query).toEqual({
fql: [{ value: "bar" }, ".length == ", { value: { "@int": "20" } }],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses nested expressions", () => {
Expand All @@ -104,7 +122,7 @@ describe("fql method producing Querys", () => {
{ fql: ["Math.add(", { value: { "@int": "17" } }, ", 3)"] },
],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses deep nested expressions", () => {
Expand Down Expand Up @@ -132,38 +150,7 @@ describe("fql method producing Querys", () => {
},
],
});
expect(queryRequest.arguments).toStrictEqual({});
});

it("adds headers if passed in", () => {
const str = "baz";
const num = 17;
const innerQuery = fql`Math.add(${num}, 3)`;
const queryBuilder = fql`${str}.length == ${innerQuery}`;
const queryRequest = queryBuilder.toQuery({
linearized: true,
query_timeout_ms: 600,
max_contention_retries: 4,
query_tags: { a: "tag" },
traceparent: "00-750efa5fb6a131eb2cf4db39f28366cb-5669e71839eca76b-00",
typecheck: false,
});
expect(queryRequest).toMatchObject({
linearized: true,
query_timeout_ms: 600,
max_contention_retries: 4,
query_tags: { a: "tag" },
traceparent: "00-750efa5fb6a131eb2cf4db39f28366cb-5669e71839eca76b-00",
typecheck: false,
});
expect(queryRequest.query).toEqual({
fql: [
{ value: "baz" },
".length == ",
{ fql: ["Math.add(", { value: { "@int": "17" } }, ", 3)"] },
],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});

it("parses with FQL string interpolation", async () => {
Expand All @@ -180,6 +167,6 @@ describe("fql method producing Querys", () => {
'\n "Hello, #{name}"\n ',
],
});
expect(queryRequest.arguments).toStrictEqual({});
expect(queryRequest.arguments).toBeUndefined();
});
});
Loading

0 comments on commit 5e6d7d8

Please sign in to comment.