Merge pull request #132 from TahaKhanAbdalli/filter_array_in_query
added feature to also include array as filter in GET /:name/rows route
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soul-cli",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.0",
|
||||
"description": "A SQLite REST and Realtime server",
|
||||
"main": "src/server.js",
|
||||
"bin": {
|
||||
|
||||
@@ -66,7 +66,7 @@ const listTableRows = async (req, res) => {
|
||||
_ordering,
|
||||
_schema,
|
||||
_extend,
|
||||
_filters = '',
|
||||
_filters = ''
|
||||
} = req.query;
|
||||
|
||||
const page = parseInt(_page);
|
||||
@@ -78,8 +78,11 @@ const listTableRows = async (req, res) => {
|
||||
// e.g. ?_filters=name:John,age:20
|
||||
// will filter by name like '%John%' and age like '%20%'
|
||||
let filters = [];
|
||||
|
||||
// split the filters by comma(,) except when in an array
|
||||
const re = /(\w+:?(\[.*?\]|\w+)?)/g;
|
||||
try {
|
||||
filters = _filters.split(',').map((filter) => {
|
||||
filters = _filters.match(re)?.map((filter) => {
|
||||
let [key, value] = filter.split(':');
|
||||
|
||||
let field = key.split('__')[0];
|
||||
@@ -89,7 +92,7 @@ const listTableRows = async (req, res) => {
|
||||
fieldOperator = 'eq';
|
||||
} else if (!operators[fieldOperator]) {
|
||||
throw new Error(
|
||||
`Invalid field operator "${fieldOperator}" for field "${field}". You can only use the following operators after the "${field}" field: __lt, __gt, __lte, __gte, __eq, __neq.`
|
||||
`Invalid field operator '${fieldOperator}' for field '${field}'. You can only use the following operators after the '${field}' field: __lt, __gt, __lte, __gte, __eq, __neq.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ const listTableRows = async (req, res) => {
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
message: error.message,
|
||||
error: error,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,15 +117,18 @@ const listTableRows = async (req, res) => {
|
||||
whereString += ' WHERE ';
|
||||
whereString += filters
|
||||
.map((filter) => {
|
||||
let query;
|
||||
if (filter.value != null) {
|
||||
query = `${tableName}.${filter.field} ${filter.operator} ?`;
|
||||
whereStringValues.push(filter.value);
|
||||
if (filter.value) {
|
||||
if (filter.value.startsWith('[') && filter.value.endsWith(']')) {
|
||||
const arrayValues = filter.value.slice(1, -1).split(',');
|
||||
return `${tableName}.${filter.field} IN (${arrayValues
|
||||
.map((val) => `'${val}'`)
|
||||
.join(', ')})`;
|
||||
} else {
|
||||
return `${tableName}.${filter.field} ${filter.operator} '${filter.value}'`;
|
||||
}
|
||||
} else {
|
||||
query = `${tableName}.${filter.field} ${filter.operator}`;
|
||||
return `${tableName}.${filter.field} ${filter.operator}`;
|
||||
}
|
||||
|
||||
return query;
|
||||
})
|
||||
.join(' AND ');
|
||||
params = `_filters=${_filters}&`;
|
||||
@@ -149,7 +155,7 @@ const listTableRows = async (req, res) => {
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
message: error.message,
|
||||
error: error,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -265,7 +271,7 @@ const listTableRows = async (req, res) => {
|
||||
if (foreignKeyError.error) {
|
||||
return res.status(400).json({
|
||||
message: foreignKeyError.message,
|
||||
error: foreignKeyError.error,
|
||||
error: foreignKeyError.error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -319,12 +325,12 @@ const listTableRows = async (req, res) => {
|
||||
data,
|
||||
total,
|
||||
next,
|
||||
previous,
|
||||
previous
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
message: error.message,
|
||||
error: error,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -346,7 +352,7 @@ const insertRowInTable = async (req, res, next) => {
|
||||
in: 'body',
|
||||
required: true,
|
||||
type: 'object',
|
||||
schema: { $ref: "#/definitions/InsertRowRequestBody" }
|
||||
schema: { $ref: '#/definitions/InsertRowRequestBody' }
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -365,7 +371,7 @@ const insertRowInTable = async (req, res, next) => {
|
||||
#swagger.responses[201] = {
|
||||
description: 'Row inserted successfully',
|
||||
schema: {
|
||||
$ref: "#/definitions/InsertRowSuccessResponse"
|
||||
$ref: '#/definitions/InsertRowSuccessResponse'
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -386,7 +392,7 @@ const insertRowInTable = async (req, res, next) => {
|
||||
#swagger.responses[400] = {
|
||||
description: 'Bad request',
|
||||
schema: {
|
||||
$ref: "#/definitions/InsertRowErrorResponse"
|
||||
$ref: '#/definitions/InsertRowErrorResponse'
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -613,7 +619,7 @@ const updateRowInTableByPK = async (req, res, next) => {
|
||||
in: 'body',
|
||||
required: true,
|
||||
type: 'object',
|
||||
schema: { $ref: "#/definitions/UpdateRowRequestBody" }
|
||||
schema: { $ref: '#/definitions/UpdateRowRequestBody' }
|
||||
}
|
||||
|
||||
#swagger.parameters['_lookup_field'] = {
|
||||
|
||||
@@ -102,19 +102,40 @@ describe('Rows Endpoints', () => {
|
||||
expect(res.type).toEqual(expect.stringContaining('json'));
|
||||
});
|
||||
|
||||
it('POST /tables/:name/rows should insert a new row if any of the value of the object being inserted is null', async () => {
|
||||
const res = await requestWithSupertest
|
||||
.post("/api/tables/users/rows")
|
||||
.send({
|
||||
fields: {
|
||||
firstName: null,
|
||||
lastName: "Doe",
|
||||
email: null,
|
||||
username: "Jane"
|
||||
}
|
||||
});
|
||||
expect(res.status).toEqual(201);
|
||||
expect(res.type).toEqual(expect.stringContaining('json'));
|
||||
expect(res.body).toHaveProperty('data');
|
||||
});
|
||||
it('POST /tables/:name/rows should insert a new row if any of the value of the object being inserted is null', async () => {
|
||||
const res = await requestWithSupertest
|
||||
.post('/api/tables/users/rows')
|
||||
.send({
|
||||
fields: {
|
||||
firstName: null,
|
||||
lastName: 'Doe',
|
||||
email: null,
|
||||
username: 'Jane'
|
||||
}
|
||||
});
|
||||
expect(res.status).toEqual(201);
|
||||
expect(res.type).toEqual(expect.stringContaining('json'));
|
||||
expect(res.body).toHaveProperty('data');
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows should return values if any of the IDs from the array match the user ID.', async () => {
|
||||
const res = await requestWithSupertest.get(
|
||||
'/api/tables/users/rows?_filters=id:[2,3]'
|
||||
);
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body).toHaveProperty('data');
|
||||
expect(res.body.data).toEqual(expect.any(Array));
|
||||
expect(res.body.data.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows should return values if the provided ID matches the user ID.', async () => {
|
||||
const res = await requestWithSupertest.get(
|
||||
'/api/tables/users/rows?_filters=id:2,firstName:Michael,lastName:Lee'
|
||||
);
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body).toHaveProperty('data');
|
||||
expect(res.body.data).toEqual(expect.any(Array));
|
||||
expect(res.body.data.length).toEqual(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ Response
|
||||
- `_ordering` e.g. `?_ordering=-Title`, to order rows by title descending, or without `-` to sort ascending, e.g. `?_ordering=Title`
|
||||
- `_schema` e.g. `?_schema=Title,ArtistId`, to get only the Title and ArtistId columns.
|
||||
- `_extend` e.g. `?_extend=ArtistId`, to get the Artist object related to the Album.
|
||||
- `_filters` e.g. `?_filters=ArtistId:1,Title:Rock`, to get only the rows where the ArtistId is 1 and the Title is Rock.
|
||||
- `_filters` e.g. `?_filters=ArtistId:[1,2,3],Title:Rock`, to get only the rows where the ArtistId can be 1,2 or 3 and the Title is Rock.
|
||||
NOTE: If you want to use comparison operators in the filter, you can use these operators after the field: `__eq, __neq, __lt, __gt, __lte, __gte, __null, __notNull` . For example, you can use `/invoices/rows?_filters=InvoiceId__neq:1,Total__gte:5,BillingPostalCode__notNull`
|
||||
|
||||
Example with query params
|
||||
|
||||
Reference in New Issue
Block a user