Skip to content

Commit

Permalink
add client hook tests
Browse files Browse the repository at this point in the history
  • Loading branch information
prostgles committed Mar 30, 2024
1 parent 9d74de9 commit 4ab4f59
Show file tree
Hide file tree
Showing 15 changed files with 1,786 additions and 245 deletions.
6 changes: 3 additions & 3 deletions lib/DboBuilder/QueryBuilder/QueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type SelectItem = {
columnName?: undefined;
});
export type SelectItemValidated = SelectItem & { fields: string[]; }

export type WhereOptions = Awaited<ReturnType<ViewHandler["prepareWhere"]>>;
export type NewQueryRoot = {
/**
* All fields from the table will be in nested SELECT and GROUP BY to allow order/filter by fields not in select
Expand All @@ -41,7 +41,7 @@ export type NewQueryRoot = {

table: string;
where: string;
whereOpts: Awaited<ReturnType<ViewHandler["prepareWhere"]>>;
whereOpts: WhereOptions;
orderByItems: SortItem[];
having: string;
limit: number | null;
Expand Down Expand Up @@ -246,7 +246,7 @@ export class SelectItemBuilder {
*/
if(
(typeof val === "string" && val !== "*") ||
isPlainObject(val) && Object.keys(val).length === 1 && Array.isArray(Object.values(val)[0]) // !isPlainObject(Object.values(val)[0])
isPlainObject(val) && Object.keys(val).length === 1 && Array.isArray(Object.values(val)[0])
){

let funcName: string | undefined, args: any[] | undefined;
Expand Down
14 changes: 4 additions & 10 deletions lib/DboBuilder/QueryBuilder/getNewQuery.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { DetailedJoinSelect, JoinPath, JoinSelect, RawJoinPath, SelectParams, SimpleJoinSelect, getKeys } from "prostgles-types";
import { Filter, LocalParams } from "../DboBuilder";
import { TableRule } from "../../PublishParser/PublishParser";
import { get } from "../../utils";
import { ViewHandler } from "../ViewHandler/ViewHandler";
import { parseJoinPath } from "../ViewHandler/parseJoinPath";
import { prepareSortItems } from "../ViewHandler/prepareSortItems";
Expand Down Expand Up @@ -79,7 +78,6 @@ export async function getNewQuery(
const allowedOrderByFields = !tableRules? _this.column_names.slice(0) : _this.parseFieldFilter(tableRules?.select?.orderByFields ?? tableRules?.select?.fields);
const allowedSelectFields = !tableRules? _this.column_names.slice(0) : _this.parseFieldFilter(tableRules?.select?.fields);


const joinQueries: NewQueryJoin[] = [];

const { select: userSelect = "*" } = selectParams,
Expand Down Expand Up @@ -186,14 +184,14 @@ export async function getNewQuery(
const filterOpts = await _this.prepareWhere({
filter,
select,
forcedFilter: get(tableRules, "select.forcedFilter"),
filterFields: get(tableRules, "select.filterFields"),
forcedFilter: tableRules?.select?.forcedFilter,
filterFields: tableRules?.select?.filterFields,
tableAlias: selectParams.alias,
localParams,
tableRule: tableRules
});
const where = filterOpts.where;
const p = _this.getValidatedRules(tableRules, localParams);
const validatedRules = _this.getValidatedRules(tableRules, localParams);

const resQuery: NewQuery = {
/** Why was this the case? */
Expand All @@ -207,15 +205,11 @@ export async function getNewQuery(
whereOpts: filterOpts,
having: "",
isLeftJoin: false,
// having: cond.having,
limit: _this.prepareLimitQuery(selectParams.limit, p),
limit: _this.prepareLimitQuery(selectParams.limit, validatedRules),
orderByItems: prepareSortItems(selectParams.orderBy, allowedOrderByFields, selectParams.alias, select, joinQueries),
offset: _this.prepareOffsetQuery(selectParams.offset)
};

// console.log(resQuery);
// console.log(buildJoinQuery(_this, resQuery));

if(resQuery.select.some(s => s.type === "aggregation") && resQuery.joins?.length){
throw `Root query aggregation AND nested joins not allowed`;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/DboBuilder/dboBuilderUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function parseError(e: any, caller: string): ProstglesError {
const result: ProstglesError = {
...errorObject,
message,
stack,
// stack,
}
return result;
}
Expand Down
54 changes: 28 additions & 26 deletions lib/DboBuilder/getCondition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export async function getCondition(
});

let allowedSelect: SelectItem[] = [];
/* Select aliases take precedence over col names. This is to ensure filters work correctly and even on computed cols*/
/* Select aliases take precedence over col names. This is to ensure filters work correctly even on computed cols*/
if (select) {
/* Allow filtering by selected fields/funcs */
allowedSelect = select.filter(s => {
Expand All @@ -96,23 +96,24 @@ export async function getCondition(
}

/* Add remaining allowed fields */
const remainingNonSelectedColumns: SelectItem[] = p.allColumns.filter(c =>
allowed_colnames.includes(c.name) &&
!allowedSelect.find(s => s.alias === c.name)
).map(f => ({
type: f.type,
alias: f.name,
columnName: f.type === "column"? f.name : undefined as any,
getQuery: (tableAlias) => f.getQuery({
tableAlias,
allColumns: this.columns,
allowedFields: allowed_colnames
}),
selected: false,
getFields: () => [f.name],
column_udt_type: f.type === "column" ? this.columns.find(c => c.name === f.name)?.udt_name : undefined
}))
allowedSelect = allowedSelect.concat(
p.allColumns.filter(c =>
allowed_colnames.includes(c.name) &&
!allowedSelect.find(s => s.alias === c.name)
).map(f => ({
type: f.type,
alias: f.name,
columnName: f.type === "column"? f.name : undefined as any,
getQuery: (tableAlias) => f.getQuery({
tableAlias,
allColumns: this.columns,
allowedFields: allowed_colnames
}),
selected: false,
getFields: () => [f.name],
column_udt_type: f.type === "column" ? this.columns.find(c => c.name === f.name)?.udt_name : undefined
}))
remainingNonSelectedColumns
);

/* Parse complex filters
Expand Down Expand Up @@ -180,15 +181,14 @@ export async function getCondition(
will make an exists filter
*/

const filterKeys = Object.keys(filter).filter(k => k !== complexFilterKey && !funcFilter.find(ek => ek.name === k) && !computedFields.find(cf => cf.name === k) && !existsConfigs.find(ek => ek.existType === k));
// if(allowed_colnames){
// const aliasedColumns = (select || []).filter(s =>
// ["function", "computed", "column"].includes(s.type) && allowed_colnames.includes(s.alias) ||
// s.getFields().find(f => allowed_colnames.includes(f))
// ).map(s => s.alias);
// const validCols = [...allowed_colnames, ...aliasedColumns];
const filterKeys = Object.keys(filter)
.filter(k =>
k !== complexFilterKey &&
!funcFilter.find(ek => ek.name === k) &&
!computedFields.find(cf => cf.name === k) &&
!existsConfigs.find(ek => ek.existType === k)
);

// }
const validFieldNames = allowedSelect.map(s => s.alias);
const invalidColumn = filterKeys
.find(fName => !validFieldNames.find(c =>
Expand All @@ -202,7 +202,9 @@ export async function getCondition(
));

if (invalidColumn) {
throw `Table: ${this.name} -> disallowed/inexistent columns in filter: ${invalidColumn} \n Expecting one of: ${allowedSelect.map(s => s.type === "column" ? s.getQuery() : s.alias).join(", ")}`;
const allowedCols = allowedSelect.map(s => s.type === "column" ? s.getQuery() : s.alias).join(", ");
const errMessage = `Table: ${this.name} -> disallowed/inexistent columns in filter: ${invalidColumn} \n Expecting one of: ${allowedCols}`;
throw errMessage;
}

/* TODO: Allow filter funcs */
Expand Down
16 changes: 6 additions & 10 deletions lib/DboBuilder/getSubscribeRelatedTables.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AnyObject, asName, reverseParsedPath, SubscribeParams } from "prostgles-types";
import { ExistsFilterConfig, Filter, LocalParams, getClientErrorFromPGError } from "./DboBuilder";
import { TableRule } from "../PublishParser/PublishParser";
import { log, ViewSubscriptionOptions } from "../PubSubManager/PubSubManager";
import { Filter, getClientErrorFromPGError, LocalParams } from "./DboBuilder";
import { NewQuery } from "./QueryBuilder/QueryBuilder";
import { ViewHandler } from "./ViewHandler/ViewHandler";

Expand All @@ -10,17 +10,14 @@ type Args = {
filter: Filter;
table_rules: TableRule<AnyObject, void> | undefined;
localParams: LocalParams | undefined;
condition: string;
filterOpts: {
where: string;
filter: AnyObject;
exists: ExistsFilterConfig[];
};
newQuery: NewQuery;
}
export async function getSubscribeRelatedTables(this: ViewHandler, { selectParams, filter, localParams, table_rules, condition, filterOpts }: Args){
export async function getSubscribeRelatedTables(this: ViewHandler, { selectParams, filter, localParams, table_rules, newQuery }: Args){

let viewOptions: ViewSubscriptionOptions | undefined = undefined;
const { condition } = newQuery.whereOpts;
if (this.is_view) {
/** TODO: this needs to be memoized on schema fetch */
const viewName = this.name;
const viewNameEscaped = this.escapedName;
const { current_schema } = await this.db.oneOrNone("SELECT current_schema")
Expand Down Expand Up @@ -129,15 +126,14 @@ export async function getSubscribeRelatedTables(this: ViewHandler, { selectParam

/** Any joined table used within select or filter must also be added a trigger for this recordset */
} else {
const newQuery = await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, { ...localParams, returnNewQuery: true }) as unknown as NewQuery;
viewOptions = {
type: "table",
relatedTables: []
}
/**
* Avoid nested exists error. Will affect performance
*/
const nonExistsFilter = filterOpts.exists.length ? {} : filter;
const nonExistsFilter = newQuery.whereOpts.exists.length ? {} : filter;
for await (const j of (newQuery.joins ?? [])) {
if (!viewOptions!.relatedTables.find(rt => rt.tableName === j.table)) {
/**
Expand Down
26 changes: 14 additions & 12 deletions lib/DboBuilder/subscribe.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AnyObject, SubscribeParams, SubscriptionChannels } from "prostgles-types";
import { Filter, LocalParams, parseError } from "./DboBuilder";
import { TableRule } from "../PublishParser/PublishParser";
import { omitKeys } from "../PubSubManager/PubSubManager";
import { Filter, LocalParams, parseError } from "./DboBuilder";
import { NewQuery } from "./QueryBuilder/QueryBuilder";
import { ViewHandler } from "./ViewHandler/ViewHandler";
import { getSubscribeRelatedTables } from "./getSubscribeRelatedTables";

Expand Down Expand Up @@ -42,10 +42,10 @@ async function subscribe(this: ViewHandler, filter: Filter, params: SubscribePar
throw " Cannot have localFunc AND socket ";
}

const { filterFields, forcedFilter } = table_rules?.select || {},
filterOpts = await this.prepareWhere({ filter, forcedFilter, addWhere: false, filterFields, tableAlias: undefined, localParams, tableRule: table_rules }),
condition = filterOpts.where,
{ throttle = 0, throttleOpts, ...selectParams } = params;
const { throttle = 0, throttleOpts, ...selectParams } = params;

/** Ensure request is valid */
await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams);

/** app_triggers condition field has an index which limits it's value.
* TODO: use condition md5 hash
Expand All @@ -58,16 +58,18 @@ async function subscribe(this: ViewHandler, filter: Filter, params: SubscribePar
throw "Subscribe not possible. Must be superuser to add triggers 1856";
}

/** Ensure request is valid */
await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams);

const viewOptions = await getSubscribeRelatedTables.bind(this)({ filter, selectParams, table_rules, localParams, condition, filterOpts })
const newQuery: NewQuery = await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, { ...localParams, returnNewQuery: true }) as any;
const viewOptions = await getSubscribeRelatedTables.bind(this)({
filter, selectParams,
table_rules, localParams,
newQuery
});

const commonSubOpts = {
table_info: this.tableOrViewInfo,
viewOptions,
table_rules,
condition,
condition: newQuery.whereOpts.condition,
table_name: this.name,
filter: { ...filter },
params: { ...selectParams },
Expand Down Expand Up @@ -109,4 +111,4 @@ async function subscribe(this: ViewHandler, filter: Filter, params: SubscribePar
}
}

export { subscribe }
export { subscribe };
4 changes: 0 additions & 4 deletions lib/Filtering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,9 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
* geom && st_makeenvelope(funcArgs)
*/
const filterValueKeys = Object.keys(filterValue);
// if(!filterValueKeys.length || filterValueKeys.length !== 1){
// return mErr("Bad filter. Expecting a nested object with one key only but got: " + JSON.stringify(filterValue, null, 2));
// }
funcName = filterValueKeys[0] as any;
if(ALLOWED_FUNCS.includes(funcName as any)){
funcArgs = filterValue[funcName as any];
// return mErr(`Bad filter. Nested function ${funcName} could not be found. Expecting one of: ${ALLOWED_FUNCS}`);
} else {
funcName = undefined;
}
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "prostgles-server",
"version": "4.2.34",
"version": "4.2.35",
"description": "",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -46,7 +46,7 @@
"pg-cursor": "^2.10.3",
"pg-promise": "^11.5.4",
"prostgles-client": "^4.0.53",
"prostgles-types": "^4.0.71"
"prostgles-types": "^4.0.73"
},
"devDependencies": {
"@types/bluebird": "^3.5.36",
Expand Down
Loading

0 comments on commit 4ab4f59

Please sign in to comment.