Skip to content

Commit 8d0500f

Browse files
committed
feat: add support for raw sql filter
1 parent 3e6e135 commit 8d0500f

File tree

3 files changed

+41
-8
lines changed

3 files changed

+41
-8
lines changed

adminforth/dataConnectors/baseConnector.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,17 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
5656
}, { ok: true, error: '' });
5757
}
5858

59-
if (!filters.operator) {
60-
return { ok: false, error: `Field "operator" not specified in filter object: ${JSON.stringify(filters)}` };
61-
}
62-
6359
if ((filters as IAdminForthSingleFilter).field) {
6460
// if "field" is present, filter must be Single
61+
if (!filters.operator) {
62+
return { ok: false, error: `Field "operator" not specified in filter object: ${JSON.stringify(filters)}` };
63+
}
64+
if ((filters as IAdminForthSingleFilter).value === undefined) {
65+
return { ok: false, error: `Field "value" not specified in filter object: ${JSON.stringify(filters)}` };
66+
}
67+
if ((filters as IAdminForthSingleFilter).insecureRawSQL) {
68+
return { ok: false, error: `Field "insecureRawSQL" should not be specified in filter object alongside "field": ${JSON.stringify(filters)}` };
69+
}
6570
if (![AdminForthFilterOperators.EQ, AdminForthFilterOperators.NE, AdminForthFilterOperators.GT,
6671
AdminForthFilterOperators.LT, AdminForthFilterOperators.GTE, AdminForthFilterOperators.LTE,
6772
AdminForthFilterOperators.LIKE, AdminForthFilterOperators.ILIKE, AdminForthFilterOperators.IN,
@@ -73,6 +78,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
7378
const similar = suggestIfTypo(resource.dataSourceColumns.map((col) => col.name), (filters as IAdminForthSingleFilter).field);
7479
throw new Error(`Field '${(filters as IAdminForthSingleFilter).field}' not found in resource '${resource.resourceId}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
7580
}
81+
// value normalization
7682
if (filters.operator == AdminForthFilterOperators.IN || filters.operator == AdminForthFilterOperators.NIN) {
7783
if (!Array.isArray(filters.value)) {
7884
return { ok: false, error: `Value for operator '${filters.operator}' should be an array, in filter object: ${JSON.stringify(filters) }` };
@@ -85,8 +91,19 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
8591
} else {
8692
(filters as IAdminForthSingleFilter).value = this.setFieldValue(fieldObj, (filters as IAdminForthSingleFilter).value);
8793
}
94+
} else if ((filters as IAdminForthSingleFilter).insecureRawSQL) {
95+
// if "insecureRawSQL" filter is insecure sql string
96+
if ((filters as IAdminForthSingleFilter).operator) {
97+
return { ok: false, error: `Field "operator" should not be specified in filter object alongside "insecureRawSQL": ${JSON.stringify(filters)}` };
98+
}
99+
if ((filters as IAdminForthSingleFilter).value !== undefined) {
100+
return { ok: false, error: `Field "value" should not be specified in filter object alongside "insecureRawSQL": ${JSON.stringify(filters)}` };
101+
}
88102
} else if ((filters as IAdminForthAndOrFilter).subFilters) {
89103
// if "subFilters" is present, filter must be AndOr
104+
if (!filters.operator) {
105+
return { ok: false, error: `Field "operator" not specified in filter object: ${JSON.stringify(filters)}` };
106+
}
90107
if (![AdminForthFilterOperators.AND, AdminForthFilterOperators.OR].includes(filters.operator)) {
91108
return { ok: false, error: `Field "operator" has wrong value in filter object: ${JSON.stringify(filters)}` };
92109
}

adminforth/dataConnectors/postgres.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,14 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
219219
return `${field} ${operator} ${placeholder}`;
220220
}
221221

222+
// filter is a single insecure raw sql
223+
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
224+
return (filter as IAdminForthSingleFilter).insecureRawSQL;
225+
}
226+
222227
// filter is a AndOr filter
223228
return (filter as IAdminForthAndOrFilter).subFilters.map((f) => {
224-
if ((f as IAdminForthSingleFilter).field) {
229+
if ((f as IAdminForthSingleFilter).field || (f as IAdminForthSingleFilter).insecureRawSQL) {
225230
// subFilter is a Single filter
226231
return this.getFilterString(resource, f);
227232
}
@@ -243,6 +248,11 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
243248
}
244249
}
245250

251+
// filter is a single insecure raw sql
252+
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
253+
return [];
254+
}
255+
246256
// filter is a AndOrFilter
247257
return (filter as IAdminForthAndOrFilter).subFilters.reduce((params: any[], f: IAdminForthSingleFilter | IAdminForthAndOrFilter) => {
248258
return params.concat(this.getFilterParams(f));
@@ -291,6 +301,11 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
291301

292302
async getCount({ resource, filters }: { resource: AdminForthResource; filters: IAdminForthAndOrFilter; }): Promise<number> {
293303
const tableName = resource.table;
304+
// validate and normalize in case this method is called from dataAPI
305+
const filterValidation = this.validateAndNormalizeFilters(filters, resource);
306+
if (!filterValidation.ok) {
307+
throw new Error(filterValidation.error);
308+
}
294309
const { sql: where, values: filterValues } = this.whereClauseAndValues(resource, filters);
295310
const q = `SELECT COUNT(*) FROM "${tableName}" ${where}`;
296311
if (process.env.HEAVY_DEBUG_QUERY) {

adminforth/types/Back.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,13 @@ export interface IExpressHttpServer extends IHttpServer {
107107

108108

109109
export interface IAdminForthSingleFilter {
110-
field: string;
111-
operator: AdminForthFilterOperators.EQ | AdminForthFilterOperators.NE
110+
field?: string;
111+
operator?: AdminForthFilterOperators.EQ | AdminForthFilterOperators.NE
112112
| AdminForthFilterOperators.GT | AdminForthFilterOperators.LT | AdminForthFilterOperators.GTE
113113
| AdminForthFilterOperators.LTE | AdminForthFilterOperators.LIKE | AdminForthFilterOperators.ILIKE
114114
| AdminForthFilterOperators.IN | AdminForthFilterOperators.NIN;
115-
value: any;
115+
value?: any;
116+
insecureRawSQL?: string;
116117
}
117118
export interface IAdminForthAndOrFilter {
118119
operator: AdminForthFilterOperators.AND | AdminForthFilterOperators.OR;

0 commit comments

Comments
 (0)