postgres sanitize query value
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { Field, TabAsField } from 'payload/dist/fields/config/types';
|
import { Field, TabAsField } from 'payload/dist/fields/config/types';
|
||||||
import { createArrayFromCommaDelineated } from '../utilities/createArrayFromCommaDelineated';
|
import { createArrayFromCommaDelineated } from 'payload/dist/utilities/createArrayFromCommaDelineated';
|
||||||
|
|
||||||
type SanitizeQueryValueArgs = {
|
type SanitizeQueryValueArgs = {
|
||||||
field: Field | TabAsField
|
field: Field | TabAsField
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
export function createArrayFromCommaDelineated(input: string): string[] {
|
|
||||||
if (Array.isArray(input)) return input;
|
|
||||||
if (input.indexOf(',') > -1) {
|
|
||||||
return input.split(',');
|
|
||||||
}
|
|
||||||
return [input];
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ export const find: Find = async function find(
|
|||||||
collection,
|
collection,
|
||||||
where: incomingWhere,
|
where: incomingWhere,
|
||||||
page = 1,
|
page = 1,
|
||||||
limit: limitArg,
|
limit,
|
||||||
sort: sortArg,
|
sort: sortArg,
|
||||||
locale,
|
locale,
|
||||||
pagination,
|
pagination,
|
||||||
@@ -23,7 +23,6 @@ export const find: Find = async function find(
|
|||||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config;
|
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config;
|
||||||
const tableName = toSnakeCase(collection);
|
const tableName = toSnakeCase(collection);
|
||||||
const table = this.tables[tableName];
|
const table = this.tables[tableName];
|
||||||
const limit = typeof limitArg === 'number' ? limitArg : collectionConfig.admin.pagination.defaultLimit;
|
|
||||||
const sort = typeof sortArg === 'string' ? sortArg : collectionConfig.defaultSort;
|
const sort = typeof sortArg === 'string' ? sortArg : collectionConfig.defaultSort;
|
||||||
let totalDocs: number;
|
let totalDocs: number;
|
||||||
let totalPages: number;
|
let totalPages: number;
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import { a as SQL } from 'drizzle-orm/column.d-aa4e525d';
|
|
||||||
import { Field } from 'payload/dist/fields/config/types';
|
|
||||||
import { PostgresAdapter } from '../types';
|
|
||||||
import { BuildQueryJoins } from './buildQuery';
|
|
||||||
|
|
||||||
type SearchParam = {
|
|
||||||
path?: string,
|
|
||||||
value: SQL,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove async
|
|
||||||
/**
|
|
||||||
* Convert the Payload key / value / operator into a Drizzle query
|
|
||||||
*/
|
|
||||||
export async function buildSearchParam({
|
|
||||||
joins,
|
|
||||||
fields,
|
|
||||||
incomingPath,
|
|
||||||
val,
|
|
||||||
operator,
|
|
||||||
collectionSlug,
|
|
||||||
globalSlug,
|
|
||||||
adapter,
|
|
||||||
locale,
|
|
||||||
}: {
|
|
||||||
fields: Field[],
|
|
||||||
joins: BuildQueryJoins,
|
|
||||||
incomingPath: string,
|
|
||||||
val: unknown,
|
|
||||||
operator: string
|
|
||||||
collectionSlug?: string,
|
|
||||||
globalSlug?: string,
|
|
||||||
adapter: PostgresAdapter
|
|
||||||
locale?: string
|
|
||||||
sort?: string
|
|
||||||
}): Promise<SQL> {
|
|
||||||
// Replace GraphQL nested field double underscore formatting
|
|
||||||
const sanitizedPath = incomingPath.replace(/__/gi, '.');
|
|
||||||
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// - switch on field type
|
|
||||||
// - add joins
|
|
||||||
// return operatorKey(table[path], formattedValue);
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
type
|
|
||||||
@@ -9,6 +9,7 @@ import { PostgresAdapter } from '../types';
|
|||||||
import { operatorMap } from './operatorMap';
|
import { operatorMap } from './operatorMap';
|
||||||
import { BuildQueryJoins } from './buildQuery';
|
import { BuildQueryJoins } from './buildQuery';
|
||||||
import { getTableColumnFromPath } from './getTableColumnFromPath';
|
import { getTableColumnFromPath } from './getTableColumnFromPath';
|
||||||
|
import { sanitizeQueryValue } from './sanitizeQueryValue';
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
joins: BuildQueryJoins
|
joins: BuildQueryJoins
|
||||||
@@ -66,7 +67,12 @@ export async function parseParams({
|
|||||||
pathSegments: relationOrPath.split('.'),
|
pathSegments: relationOrPath.split('.'),
|
||||||
tableName,
|
tableName,
|
||||||
});
|
});
|
||||||
constraints.push(operatorMap[operator](tableColumn.table[tableColumn.columnName], where[relationOrPath][operator]));
|
const queryValue = sanitizeQueryValue({
|
||||||
|
field: tableColumn.field,
|
||||||
|
operator,
|
||||||
|
val: where[relationOrPath][operator],
|
||||||
|
});
|
||||||
|
constraints.push(operatorMap[operator](tableColumn.table[tableColumn.columnName], queryValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
95
packages/db-postgres/src/queries/sanitizeQueryValue.ts
Normal file
95
packages/db-postgres/src/queries/sanitizeQueryValue.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { Field, TabAsField } from 'payload/dist/fields/config/types';
|
||||||
|
import { createArrayFromCommaDelineated } from 'payload/dist/utilities/createArrayFromCommaDelineated';
|
||||||
|
import { APIError } from 'payload/errors';
|
||||||
|
|
||||||
|
type SanitizeQueryValueArgs = {
|
||||||
|
field: Field | TabAsField
|
||||||
|
operator: string
|
||||||
|
val: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sanitizeQueryValue = ({ field, operator, val }: SanitizeQueryValueArgs): unknown => {
|
||||||
|
let formattedValue = val;
|
||||||
|
|
||||||
|
// // Disregard invalid _ids
|
||||||
|
// if (path === '_id' && typeof val === 'string' && val.split(',').length === 1) {
|
||||||
|
// if (!hasCustomID) {
|
||||||
|
// const isValid = mongoose.Types.ObjectId.isValid(val);
|
||||||
|
//
|
||||||
|
// if (!isValid) {
|
||||||
|
// return undefined;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (field.type === 'number') {
|
||||||
|
// const parsedNumber = parseFloat(val);
|
||||||
|
//
|
||||||
|
// if (Number.isNaN(parsedNumber)) {
|
||||||
|
// return undefined;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Cast incoming values as proper searchable types
|
||||||
|
if (field.type === 'checkbox' && typeof val === 'string') {
|
||||||
|
if (val.toLowerCase() === 'true') formattedValue = true;
|
||||||
|
if (val.toLowerCase() === 'false') formattedValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['all', 'not_in', 'in'].includes(operator) && typeof formattedValue === 'string') {
|
||||||
|
formattedValue = createArrayFromCommaDelineated(formattedValue);
|
||||||
|
|
||||||
|
if (field.type === 'number') {
|
||||||
|
formattedValue = formattedValue.map((arrayVal) => parseFloat(arrayVal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.type === 'number' && typeof formattedValue === 'string') {
|
||||||
|
formattedValue = Number(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.type === 'date' && typeof val === 'string') {
|
||||||
|
formattedValue = new Date(val);
|
||||||
|
if (Number.isNaN(Date.parse(formattedValue))) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (['relationship', 'upload'].includes(field.type)) {
|
||||||
|
if (val === 'null') {
|
||||||
|
formattedValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (operator === 'in' && Array.isArray(formattedValue)) {
|
||||||
|
// formattedValue = formattedValue.reduce((formattedValues, inVal) => {
|
||||||
|
// const newValues = [inVal];
|
||||||
|
// if (mongoose.Types.ObjectId.isValid(inVal)) newValues.push(new mongoose.Types.ObjectId(inVal));
|
||||||
|
//
|
||||||
|
// const parsedNumber = parseFloat(inVal);
|
||||||
|
// if (!Number.isNaN(parsedNumber)) newValues.push(parsedNumber);
|
||||||
|
//
|
||||||
|
// return [
|
||||||
|
// ...formattedValues,
|
||||||
|
// ...newValues,
|
||||||
|
// ];
|
||||||
|
// }, []);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operator === 'near' || operator === 'within' || operator === 'intersects') {
|
||||||
|
throw new APIError(`Querying with '${operator}' is not supported with the postgres database adapter.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (path !== '_id' || (path === '_id' && hasCustomID && field.type === 'text')) {
|
||||||
|
// if (operator === 'contains') {
|
||||||
|
// formattedValue = { $regex: formattedValue, $options: 'i' };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (operator === 'exists') {
|
||||||
|
formattedValue = (formattedValue === 'true' || formattedValue === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedValue;
|
||||||
|
};
|
||||||
@@ -234,7 +234,7 @@ describe('collections-rest', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { status, docs } = await client.deleteMany<Post>({
|
const { status, docs } = await client.deleteMany<Post>({
|
||||||
where: { title: { eq: 'title' } },
|
where: { title: { equals: 'title' } },
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
|
|||||||
Reference in New Issue
Block a user