mirror of
https://github.com/strapi/strapi.git
synced 2025-08-31 12:23:05 +00:00
Merge branch 'master' into chore/strapi-new-improvments
This commit is contained in:
commit
250cfac82d
@ -31,6 +31,7 @@ Filters are used as a suffix of a field name:
|
||||
- `ncontains`: Doesn't contain
|
||||
- `containss`: Contains case sensitive
|
||||
- `ncontainss`: Doesn't contain case sensitive
|
||||
- `null`: Is null/Is not null
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -199,6 +199,7 @@ You can also apply different parameters to the query to make more complex querie
|
||||
- `<field>_containss`: Contains sensitive.
|
||||
- `<field>_in`: Matches any value in the array of values.
|
||||
- `<field>_nin`: Doesn't match any value in the array of values.
|
||||
- `<field>_null`: Equals null/Not equals null
|
||||
|
||||
Return the second decade of users which have an email that contains `@strapi.io` ordered by username.
|
||||
|
||||
@ -695,7 +696,6 @@ module.exports = {
|
||||
|
||||
In this example, the policy `isAuthenticated` located in the `users-permissions` plugin will be executed first. Then, the `isOwner` policy located in the `Post` API `./api/post/config/policies/isOwner.js`. Next, it will execute the `logging` policy located in `./config/policies/logging.js`. Finally, the resolver will be executed.
|
||||
|
||||
|
||||
::: note
|
||||
There is no custom resolver in that case, so it will execute the default resolver (Post.find) provided by the Shadow CRUD feature.
|
||||
:::
|
||||
|
@ -28,6 +28,7 @@ Easily filter results according to fields values.
|
||||
- `_containss`: Contains case sensitive
|
||||
- `_in`: Matches any value in the array of values
|
||||
- `_nin`: Doesn't match any value in the array of values
|
||||
- `_null`: Equals null/Not equals null
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -245,6 +245,9 @@ const buildWhereClause = ({ qb, field, operator, value }) => {
|
||||
return qb.where(field, 'like', `%${value}%`);
|
||||
case 'ncontainss':
|
||||
return qb.whereNot(field, 'like', `%${value}%`);
|
||||
case 'null': {
|
||||
return value ? qb.whereNull(field) : qb.whereNotNull(field);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unhandled whereClause : ${field} ${operator} ${value}`);
|
||||
|
@ -438,6 +438,9 @@ const buildWhereClause = ({ field, operator, value }) => {
|
||||
$not: new RegExp(val),
|
||||
},
|
||||
};
|
||||
case 'null': {
|
||||
return value ? { [field]: { $eq: null } } : { [field]: { $ne: null } };
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unhandled whereClause : ${field} ${operator} ${value}`);
|
||||
|
@ -25,6 +25,12 @@ const postModel = {
|
||||
type: 'biginteger',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'nullable',
|
||||
params: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
],
|
||||
connection: 'default',
|
||||
name: 'post',
|
||||
@ -54,8 +60,8 @@ describe('Test Graphql API End to End', () => {
|
||||
|
||||
describe('Test CRUD', () => {
|
||||
const postsPayload = [
|
||||
{ name: 'post 1', bigint: 1316130638171 },
|
||||
{ name: 'post 2', bigint: 1416130639261 },
|
||||
{ name: 'post 1', bigint: 1316130638171, nullable: 'value' },
|
||||
{ name: 'post 2', bigint: 1416130639261, nullable: null },
|
||||
];
|
||||
let data = {
|
||||
posts: [],
|
||||
@ -69,6 +75,7 @@ describe('Test Graphql API End to End', () => {
|
||||
post {
|
||||
name
|
||||
bigint
|
||||
nullable
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -100,6 +107,7 @@ describe('Test Graphql API End to End', () => {
|
||||
id
|
||||
name
|
||||
bigint
|
||||
nullable
|
||||
}
|
||||
}
|
||||
`,
|
||||
@ -126,6 +134,7 @@ describe('Test Graphql API End to End', () => {
|
||||
id
|
||||
name
|
||||
bigint
|
||||
nullable
|
||||
}
|
||||
}
|
||||
`,
|
||||
@ -147,6 +156,7 @@ describe('Test Graphql API End to End', () => {
|
||||
id
|
||||
name
|
||||
bigint
|
||||
nullable
|
||||
}
|
||||
}
|
||||
`,
|
||||
@ -168,6 +178,7 @@ describe('Test Graphql API End to End', () => {
|
||||
id
|
||||
name
|
||||
bigint
|
||||
nullable
|
||||
}
|
||||
}
|
||||
`,
|
||||
@ -229,7 +240,7 @@ describe('Test Graphql API End to End', () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
name_in: ['post 1', 'post 2'],
|
||||
name_in: ['post 1', 'post 2', 'post 3'],
|
||||
},
|
||||
postsPayload,
|
||||
],
|
||||
@ -239,6 +250,18 @@ describe('Test Graphql API End to End', () => {
|
||||
},
|
||||
[postsPayload[0]],
|
||||
],
|
||||
[
|
||||
{
|
||||
nullable_null: true,
|
||||
},
|
||||
[postsPayload[1]],
|
||||
],
|
||||
[
|
||||
{
|
||||
nullable_null: false,
|
||||
},
|
||||
[postsPayload[0]],
|
||||
],
|
||||
])('List posts with where clause %o', async (where, expected) => {
|
||||
const res = await graphqlQuery({
|
||||
query: /* GraphQL */ `
|
||||
@ -246,6 +269,7 @@ describe('Test Graphql API End to End', () => {
|
||||
posts(where: $where) {
|
||||
name
|
||||
bigint
|
||||
nullable
|
||||
}
|
||||
}
|
||||
`,
|
||||
@ -278,6 +302,7 @@ describe('Test Graphql API End to End', () => {
|
||||
id
|
||||
name
|
||||
bigint
|
||||
nullable
|
||||
}
|
||||
}
|
||||
`,
|
||||
@ -360,4 +385,4 @@ describe('Test Graphql API End to End', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -10,6 +10,8 @@
|
||||
const _ = require('lodash');
|
||||
const AWS = require('aws-sdk');
|
||||
|
||||
const trimParam = str => typeof str === "string" ? str.trim() : undefined
|
||||
|
||||
module.exports = {
|
||||
provider: 'aws-s3',
|
||||
name: 'Amazon Web Service S3',
|
||||
@ -55,15 +57,15 @@ module.exports = {
|
||||
init: (config) => {
|
||||
// configure AWS S3 bucket connection
|
||||
AWS.config.update({
|
||||
accessKeyId: config.public,
|
||||
secretAccessKey: config.private,
|
||||
accessKeyId: trimParam(config.public),
|
||||
secretAccessKey: trimParam(config.private),
|
||||
region: config.region
|
||||
});
|
||||
|
||||
const S3 = new AWS.S3({
|
||||
apiVersion: '2006-03-01',
|
||||
params: {
|
||||
Bucket: config.bucket
|
||||
Bucket: trimParam(config.bucket)
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -365,5 +365,33 @@ describe('convertRestQueryParams', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('Null', () => {
|
||||
expect(
|
||||
convertRestQueryParams({ 'content.text_null': true })
|
||||
).toMatchObject({
|
||||
where: [
|
||||
{
|
||||
field: 'content.text',
|
||||
operator: 'null',
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('Not Null', () => {
|
||||
expect(
|
||||
convertRestQueryParams({ 'content.text_null': false })
|
||||
).toMatchObject({
|
||||
where: [
|
||||
{
|
||||
field: 'content.text',
|
||||
operator: 'null',
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -66,7 +66,7 @@ const castValueToType = ({ type, value }) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
return value;
|
||||
return Boolean(value);
|
||||
}
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
@ -79,6 +79,17 @@ const castValueToType = ({ type, value }) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cast basic values based on attribute type
|
||||
* @param {Object} options - Options
|
||||
* @param {string} options.type - type of the atribute
|
||||
* @param {*} options.value - value tu cast
|
||||
* @param {string} options.operator - name of operator
|
||||
*/
|
||||
const castValue = ({ type, value, operator}) => {
|
||||
if (operator === 'null') return castValueToType({ type: 'boolean', value })
|
||||
return castValueToType({ type, value})
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {Object} options - Options
|
||||
@ -109,8 +120,8 @@ const buildQuery = ({ model, filters = {}, ...rest }) => {
|
||||
|
||||
// cast value or array of values
|
||||
const castedValue = Array.isArray(value)
|
||||
? value.map(val => castValueToType({ type, value: val }))
|
||||
: castValueToType({ type, value: value });
|
||||
? value.map(val => castValue({ type, operator, value: val }))
|
||||
: castValue({ type, operator, value: value });
|
||||
|
||||
return { field, operator, value: castedValue };
|
||||
});
|
||||
|
@ -132,6 +132,7 @@ const VALID_OPERATORS = [
|
||||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'null',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -81,6 +81,14 @@ const productFixtures = [
|
||||
rank: 91,
|
||||
big_rank: 926372323421,
|
||||
},
|
||||
{
|
||||
name: 'Product 4',
|
||||
description: 'Product description 4',
|
||||
price: null,
|
||||
decimal_field: 12.22,
|
||||
rank: 99,
|
||||
big_rank: 999999999999,
|
||||
},
|
||||
];
|
||||
|
||||
async function createFixtures() {
|
||||
@ -194,6 +202,35 @@ describe('Filtering API', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filter null', () => {
|
||||
test('Should return only one match', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/products',
|
||||
qs: {
|
||||
price_null: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(Array.isArray(res.body)).toBe(true);
|
||||
expect(res.body.length).toBe(1);
|
||||
expect(res.body[0]).toMatchObject(data.products[3]);
|
||||
});
|
||||
|
||||
test('Should return three matches', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/products',
|
||||
qs: {
|
||||
price_null: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(Array.isArray(res.body)).toBe(true);
|
||||
expect(res.body.length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filter contains insensitive', () => {
|
||||
test('Should match with insensitive case', async () => {
|
||||
const res1 = await rq({
|
||||
@ -985,11 +1022,14 @@ describe('Filtering API', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.body).toEqual([
|
||||
[
|
||||
data.products[3],
|
||||
data.products[0],
|
||||
data.products[2],
|
||||
data.products[1],
|
||||
]);
|
||||
].forEach(expectedPost => {
|
||||
expect(res.body).toEqual(expect.arrayContaining([expectedPost]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user