Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: error with order by test #5

Merged
merged 2 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,519 changes: 3,501 additions & 3,018 deletions package-lock.json

Large diffs are not rendered by default.

38 changes: 19 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,28 @@
},
"homepage": "https://github.com/massfords/ts-rsql-query#readme",
"devDependencies": {
"@types/common-tags": "^1.8.1",
"@types/jest": "^28.1.3",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.30.0",
"@typescript-eslint/parser": "^5.30.0",
"eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-jest": "^26.5.3",
"eslint-plugin-prettier": "^4.1.0",
"jest": "^28.1.1",
"pg-promise": "^10.11.1",
"prettier": "^2.7.1",
"@types/common-tags": "^1.8.4",
"@types/jest": "^29.5.14",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.11.0",
"@typescript-eslint/parser": "^8.11.0",
"eslint": "^8.57.1",
deadratfink marked this conversation as resolved.
Show resolved Hide resolved
"eslint-config-prettier": "^8.10.0",
"eslint-plugin-jest": "^28.8.3",
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.7.0",
"pg-promise": "^11.10.1",
"prettier": "^3.3.3",
"testcontainers": "^9.1.3",
deadratfink marked this conversation as resolved.
Show resolved Hide resolved
"ts-jest": "^28.0.5",
"ts-node": "^10.8.1",
"typescript": "^4.7.4",
"uuid": "^8.3.2"
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"typescript": "^5.6.3",
"uuid": "^11.0.0"
},
"dependencies": {
"date-fns": "^2.29.1",
"js-base64": "^3.7.2",
"tiny-invariant": "^1.2.0",
"date-fns": "^4.1.0",
"js-base64": "^3.7.7",
"tiny-invariant": "^1.3.3",
"ts-rsql": "^1.0.0"
}
}
4 changes: 2 additions & 2 deletions src/keyset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const toKeySet = (values: string[]): string => {
export const lastRowToKeySet = (
row: Record<string, unknown>,
sorts: SortNode[],
config: StaticQueryConfig
config: StaticQueryConfig,
): string[] => {
// walk each sort node
// map to the key to use to pull the value from the row
Expand All @@ -32,7 +32,7 @@ export const lastRowToKeySet = (
.map((alias) => {
invariant(
alias in row,
() => `row: ${JSON.stringify(row)} is missing property '${alias}'`
() => `row: ${JSON.stringify(row)} is missing property '${alias}'`,
);
const rowVal = row[alias];
if (typeof rowVal !== "string") {
Expand Down
5 changes: 3 additions & 2 deletions src/llb/operators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export type KnownOperator = SymbolicOperator | NamedOperator;
export const toSqlOperator = (
operator: KnownOperator,
keywordsLowerCase = false,
detachedOperators = false
detachedOperators = false,
): string => {
const space = detachedOperators ? " " : "";
switch (operator) {
Expand Down Expand Up @@ -93,11 +93,12 @@ export const isKnownOperator = (maybe: string): maybe is KnownOperator => {
* Checks if passed operator is a configured RSQL plugin operator.
*
* @param maybe - The RSQL plugin operator.
* @param plugins - The RSQL operator plugins.
* @returns A `true` if is a configured RSQL plugin operator, else `false`.
*/
export const isPluginOperator = (
maybe: string,
plugins?: RsqlOperatorPlugin[]
plugins?: RsqlOperatorPlugin[],
): boolean => {
return plugins?.length
? plugins.some((plugin) => plugin.operator.toLowerCase() === maybe)
Expand Down
6 changes: 3 additions & 3 deletions src/llb/to-orderby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { formatKeyword } from "./to-sql";
const buildRowValues = (
nodes: SortNode[],
keyset: string | null,
context: SqlContext
context: SqlContext,
): string => {
if (nodes.length === 0 || !keyset || keyset === "") {
return "";
Expand Down Expand Up @@ -42,7 +42,7 @@ export type SortResult =
export const toOrderBy = (
input: SortNode[] | string | null,
keyset: string | null,
context: SqlContext
context: SqlContext,
): SortResult => {
if (input === null) {
return { isValid: true, seek: "", orderby: "" };
Expand Down Expand Up @@ -96,7 +96,7 @@ export const toOrderBy = (

if (keyset) {
const keysetValues: string[] = JSON.parse(
Base64.decode(keyset)
Base64.decode(keyset),
) as string[];
values.push(...keysetValues);
}
Expand Down
22 changes: 11 additions & 11 deletions src/llb/to-sql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import { maybeExecuteRsqlOperatorPlugin } from "../plugin";
/**
* Formats a keyword according to configuration (either fully upper- or lower-case).
*
* @param keyword- The keyword to format.
* @param [context] - The configuration.
* @param keyword - The keyword to format.
* @param keywordsLowerCase - The configuration.
deadratfink marked this conversation as resolved.
Show resolved Hide resolved
* @returns The formatted keyword.
*/
export const formatKeyword = (
keyword: string,
keywordsLowerCase = false
keywordsLowerCase = false,
): string =>
keywordsLowerCase ? keyword.toLowerCase() : keyword.toUpperCase();

Expand All @@ -33,7 +33,7 @@ export const formatKeyword = (
*/
export const formatSelector = (
context: SqlContext,
selector: string
selector: string,
): string => {
if (!("selectors" in context)) {
return selector;
Expand All @@ -50,7 +50,7 @@ export const formatSelector = (

const selectorConfig = (
selector: string,
config: StaticQueryConfig
config: StaticQueryConfig,
): SelectorConfig | null => {
if (!config.selectors) {
return null;
Expand All @@ -64,10 +64,10 @@ const selectorConfig = (

export const formatValue = (
{ allowArray, ast }: { ast: ComparisonNode; allowArray?: boolean },
config: StaticQueryConfig
config: StaticQueryConfig,
): Value => {
const firstOperand =
ast.operands && Array.isArray(ast.operands) ? ast.operands[0] ?? "" : "";
ast.operands && Array.isArray(ast.operands) ? (ast.operands[0] ?? "") : "";
const selConfig = selectorConfig(ast.selector, config);

// case where the config is an enum
Expand Down Expand Up @@ -113,7 +113,7 @@ export const formatValue = (

export const toSql = (
input: ASTNode | string | null,
context: SqlContext
context: SqlContext,
): { isValid: true; sql: string } | { isValid: false; err: string } => {
if (!input || input === "") {
return { isValid: true, sql: "" };
Expand Down Expand Up @@ -142,7 +142,7 @@ const _toSql = (ast: ASTNode | null | Operand, context: SqlContext): string => {
const rhs = ast.operands[i] ?? null;
sql += ` ${formatKeyword(ast.operator, keywordsLowerCase)} ${_toSql(
rhs,
context
context,
)}`;
}
return `(${sql})`;
Expand All @@ -151,7 +151,7 @@ const _toSql = (ast: ASTNode | null | Operand, context: SqlContext): string => {
const operand = ast.operands[0] ?? null;
return `${formatKeyword("NOT", keywordsLowerCase)}(${_toSql(
operand,
context
context,
)})`;
} else if (isComparisonNode(ast)) {
const { values, ...config } = context;
Expand Down Expand Up @@ -201,7 +201,7 @@ const _toSql = (ast: ASTNode | null | Operand, context: SqlContext): string => {
return `${selector}${toSqlOperator(
op,
keywordsLowerCase,
detachedOperators
detachedOperators,
)}$${values.length}`;
}
case "=in=":
Expand Down
4 changes: 2 additions & 2 deletions src/llb/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import invariant from "tiny-invariant";

export const validate = (
ast: ASTNode,
context: SqlContext
context: SqlContext,
): { isValid: true } | { isValid: false; err: string } => {
if (ast.operator == "and" || ast.operator == "or") {
if (!ast.operands) {
Expand Down Expand Up @@ -133,7 +133,7 @@ const iso8601DatePattern = new RegExp(
]
.map((regex) => regex.source)
.join(""),
"u"
"u",
);
const isIso8601DateTime = (input: string): boolean => {
const parsed = parseISO(input);
Expand Down
20 changes: 10 additions & 10 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const OverwrittenOperator = {
export const maybeExecuteRsqlOperatorPlugin = (
context: SqlContext,
ast: ComparisonNode,
formattedSelector: string
formattedSelector: string,
): string | undefined => {
const { plugins, values } = context;
/* Check for plugin (custom operator or overwrite of known operator). */
Expand All @@ -49,7 +49,7 @@ export const maybeExecuteRsqlOperatorPlugin = (
isKnownOperator(ast.operator) ||
/* Case: new operator. */
(ast.operator.startsWith("=") && ast.operator.endsWith("=")),
`invalid custom RSQL operator, must start and end with '=', but was: '${ast.operator}'`
`invalid custom RSQL operator, must start and end with '=', but was: '${ast.operator}'`,
);

if (plugin.invariant) {
Expand Down Expand Up @@ -78,7 +78,7 @@ export const isBooleanValueInvariant = (ast: ComparisonNode): void => {
invariant(ast.operands[0], `operator must have one value, ${message}`);
invariant(
ast.operands[0] === "true" || ast.operands[0] === "false",
`${message}, but was: '${ast.operands[0]}'`
`${message}, but was: '${ast.operands[0]}'`,
);
};

Expand All @@ -92,7 +92,7 @@ export const isBooleanValueInvariant = (ast: ComparisonNode): void => {
* > was to have a solution for following `pg` problem: [parameterized query with an
* > `IN` operator](https://github.com/brianc/node-postgres/issues/1452).
*/
export const MapInToEqualsAnyPlugin: RsqlOperatorPlugin = {
export const MapInToEqualsAnyPlugin = {
deadratfink marked this conversation as resolved.
Show resolved Hide resolved
operator: OverwrittenOperator.IN,
invariant: (ast: ComparisonNode): void => {
invariant(ast.operands);
Expand All @@ -102,10 +102,10 @@ export const MapInToEqualsAnyPlugin: RsqlOperatorPlugin = {
values.push(formatValue({ ast, allowArray: true }, config));
return /* sql */ `${selector} = ${formatKeyword(
"ANY",
keywordsLowerCase
keywordsLowerCase,
)}($${values.length})`;
},
};
} satisfies RsqlOperatorPlugin;

/**
* Plugin for out-overwrite (not-all) operation.
Expand All @@ -117,7 +117,7 @@ export const MapInToEqualsAnyPlugin: RsqlOperatorPlugin = {
* > was to have a solution for following `pg` problem: [parameterized query with an
* > `IN` operator](https://github.com/brianc/node-postgres/issues/1452).
*/
export const MapOutToNotEqualsAllPlugin: RsqlOperatorPlugin = {
export const MapOutToNotEqualsAllPlugin = {
deadratfink marked this conversation as resolved.
Show resolved Hide resolved
operator: OverwrittenOperator.OUT,
invariant: (ast: ComparisonNode): void => {
invariant(ast.operands);
Expand All @@ -127,10 +127,10 @@ export const MapOutToNotEqualsAllPlugin: RsqlOperatorPlugin = {
values.push(formatValue({ ast, allowArray: true }, config));
return /* sql */ `${selector} <> ${formatKeyword(
"ALL",
keywordsLowerCase
keywordsLowerCase,
)}($${values.length})`;
},
};
} satisfies RsqlOperatorPlugin;

/**
* Plugin for an is-null operation.
Expand Down Expand Up @@ -210,7 +210,7 @@ export const IsNullOrEmptyPlugin: RsqlOperatorPlugin = {
reverse ? `${formatKeyword("NOT", keywordsLowerCase)} ` : ""
}(${IsNullPlugin.toSql(maybeReversedOptions)} ${formatKeyword(
"OR",
keywordsLowerCase
keywordsLowerCase,
)} ${IsEmptyPlugin.toSql(maybeReversedOptions)})`;
},
};
7 changes: 5 additions & 2 deletions src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const assembleFullQuery = (
sort: SortNode[] | string | null;
keyset: string | null;
},
context: SqlContext
context: SqlContext,
): SqlResult => {
const { filter, sort, keyset } = input;
const sqlPredicateAndOrderBy = buildPredicateAndOrderBy({
Expand All @@ -32,7 +32,10 @@ export const assembleFullQuery = (
if (sqlPredicateAndOrderBy.sql === "") {
return { isValid: true, sql: mainQuery };
}
if (sqlPredicateAndOrderBy.sql.startsWith("ORDER BY" || "order by")) {
if (
sqlPredicateAndOrderBy.sql.startsWith("ORDER BY") ||
sqlPredicateAndOrderBy.sql.startsWith("order by")
) {
deadratfink marked this conversation as resolved.
Show resolved Hide resolved
// The concat strategy is not considered here.
// We'd hit this case in a listing service w/o a predicate
// that is returning the first page of results. In this case
Expand Down
4 changes: 2 additions & 2 deletions src/tests/fixture-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ export const insertUserRecords = async (): Promise<void> => {
dob,
tier,
pointBalance,
}
},
);
})
}),
);
});
};
16 changes: 8 additions & 8 deletions src/tests/live-db.it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe("runs the sql with a real db connection", () => {
sort: null,
keyset: null,
},
context
context,
);
invariant(sql.isValid);
expect(await db.manyOrNone(sql.sql, context.values)).toHaveLength(rows);
Expand Down Expand Up @@ -177,7 +177,7 @@ describe("runs the sql with a real db connection", () => {
sort: null,
keyset: null,
},
context
context,
);
invariant(sql.isValid);
expect(await db.manyOrNone(sql.sql, context.values)).toHaveLength(rows);
Expand Down Expand Up @@ -212,7 +212,7 @@ describe("runs the sql with a real db connection", () => {
sort: null,
keyset: null,
},
context
context,
);
invariant(sql.isValid);
expect(await db.manyOrNone(sql.sql, context.values)).toHaveLength(rows);
Expand Down Expand Up @@ -275,12 +275,12 @@ describe("runs the sql with a real db connection", () => {
sort: parsedSorts,
keyset,
},
context
context,
);
invariant(sql.isValid);
const actual = await db.manyOrNone(sql.sql, context.values);
expect(
actual.map(({ firstName }: { firstName: string }) => firstName)
actual.map(({ firstName }: { firstName: string }) => firstName),
).toStrictEqual(firstNameFromEachRow);
});
});
Expand All @@ -302,20 +302,20 @@ describe("runs the sql with a real db connection", () => {
sort: parsedSorts,
keyset,
},
context
context,
);
invariant(sql.isValid);
const rows = await db.manyOrNone<UserRecord>(
`${sql.sql} limit 1`,
context.values
context.values,
);
invariant(rows.length == 1 && rows[0]);
keyset = toKeySet(lastRowToKeySet(rows[0], parsedSorts, context));
results.push(rows[0].firstName);
}
expect(results).toStrictEqual(["Charlie", "Bob", "Alice"]);
expect(keysets).toMatchInlineSnapshot(`
Array [
[
null,
"WyIzIiwiQ3VwY2FrZSIsIkNoYXJsaWUiLCIwMzk5YzcyNC01ODI5LTU0NTgtYjdhYy1hYzZhMjk4ZTBlNGIiXQ",
"WyIyIiwiQmFuYW5hIiwiQm9iIiwiNzEzOWU4MWUtZGMxMy01NGQxLThjMTAtNmZlNmY3YmZiMzRlIl0",
Expand Down
Loading
Loading