mirror of
https://github.com/directus/directus.git
synced 2026-01-26 12:07:59 -05:00
* Add depth limit to filtering * Add depth limit to GraphQL * Add docs * Rename environment variable * Add simple deep filter depth calculation * Update error message * Shift fields depth check to base function * Remove unused var * Implement GraphQL filter depth * Add check for _and & _or filters in GraphQL * Add check for _and & _or filters in REST * Remove commented code * Add check for REST filter query * Add REST tests * Setup m2m using directus fields * Add GraphQL tests * Fix linter error * Cleanup calculateDepth + add docs/tests * Remove validator in GraphQL * Add depth checking for nested sort * Enable source map to display correct error lines * Set max relational depth to be at least 2 * Update tests * Add unit test for deep _sort * Add minimum value in docs * Refactor depth validation to be in validateQuery * Add boolean parameter for calculation of _sort in deep query * Use array of keys to parse dot notation Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
349 lines
9.1 KiB
TypeScript
349 lines
9.1 KiB
TypeScript
import config, { getUrl } from '../../config';
|
|
import vendors from '../../get-dbs-to-test';
|
|
import request from 'supertest';
|
|
import knex, { Knex } from 'knex';
|
|
import { createArtist, createEvent, seedTable } from '../../setup/utils/factories';
|
|
|
|
describe('/graphql', () => {
|
|
const databases = new Map<string, Knex>();
|
|
|
|
beforeAll(async () => {
|
|
for (const vendor of vendors) {
|
|
databases.set(vendor, knex(config.knexConfig[vendor]!));
|
|
}
|
|
});
|
|
|
|
afterAll(async () => {
|
|
for (const [_vendor, connection] of databases) {
|
|
await connection.destroy();
|
|
}
|
|
});
|
|
|
|
describe('/ POST', () => {
|
|
describe('allow queries up to the field depth limit', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
const artist = createArtist();
|
|
const event = createEvent();
|
|
await seedTable(databases.get(vendor)!, 1, 'artists', artist);
|
|
await seedTable(databases.get(vendor)!, 1, 'events', event);
|
|
await seedTable(databases.get(vendor)!, 1, 'artists_events', {
|
|
artists_id: artist.id,
|
|
events_id: event.id,
|
|
});
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/graphql`)
|
|
.set('Authorization', 'Bearer AdminToken')
|
|
.set('Content-Type', 'application/json')
|
|
.send({
|
|
query: `query test {
|
|
artists (filter: {
|
|
id: { _eq: "${artist.id}" }
|
|
}) {
|
|
events {
|
|
artists_id {
|
|
events {
|
|
artists_id {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
})
|
|
.expect('Content-Type', /application\/json/)
|
|
.expect(200);
|
|
|
|
expect(response.body.data).toEqual(
|
|
expect.objectContaining({
|
|
artists: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
events: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
artists_id: expect.objectContaining({
|
|
events: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
artists_id: expect.objectContaining({
|
|
id: expect.any(String),
|
|
}),
|
|
}),
|
|
]),
|
|
}),
|
|
}),
|
|
]),
|
|
}),
|
|
]),
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('deny queries over the field depth limit', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
const artist = createArtist();
|
|
const event = createEvent();
|
|
await seedTable(databases.get(vendor)!, 1, 'artists', artist);
|
|
await seedTable(databases.get(vendor)!, 1, 'events', event);
|
|
await seedTable(databases.get(vendor)!, 1, 'artists_events', {
|
|
artists_id: artist.id,
|
|
events_id: event.id,
|
|
});
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/graphql`)
|
|
.set('Authorization', 'Bearer AdminToken')
|
|
.set('Content-Type', 'application/json')
|
|
.send({
|
|
query: `query test {
|
|
artists (filter: {
|
|
id: { _eq: "${artist.id}" }
|
|
}) {
|
|
events {
|
|
artists_id {
|
|
events {
|
|
artists_id {
|
|
events {
|
|
artists_id {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
})
|
|
.expect('Content-Type', /application\/json/)
|
|
.expect(200);
|
|
|
|
expect(response.body.errors).toBeDefined();
|
|
expect(response.body.errors.length).toEqual(1);
|
|
});
|
|
});
|
|
|
|
describe('allow queries up to filter depth limit', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
const artist = createArtist();
|
|
const event = createEvent();
|
|
await seedTable(databases.get(vendor)!, 1, 'artists', artist);
|
|
await seedTable(databases.get(vendor)!, 1, 'events', event);
|
|
await seedTable(databases.get(vendor)!, 1, 'artists_events', {
|
|
artists_id: artist.id,
|
|
events_id: event.id,
|
|
});
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/graphql`)
|
|
.set('Authorization', 'Bearer AdminToken')
|
|
.set('Content-Type', 'application/json')
|
|
.send({
|
|
query: `query test {
|
|
artists (filter: {
|
|
id: { _eq: "${artist.id}" }
|
|
}) {
|
|
events {
|
|
artists_id {
|
|
events {
|
|
artists_id (filter: { id: { _eq: "${artist.id}" } }) {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
})
|
|
.expect('Content-Type', /application\/json/)
|
|
.expect(200);
|
|
|
|
expect(response.body.data).toEqual(
|
|
expect.objectContaining({
|
|
artists: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
events: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
artists_id: expect.objectContaining({
|
|
events: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
artists_id: expect.objectContaining({
|
|
id: expect.any(String),
|
|
}),
|
|
}),
|
|
]),
|
|
}),
|
|
}),
|
|
]),
|
|
}),
|
|
]),
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('deny queries over the filter depth limit', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
const artist = createArtist();
|
|
const event = createEvent();
|
|
await seedTable(databases.get(vendor)!, 1, 'artists', artist);
|
|
await seedTable(databases.get(vendor)!, 1, 'events', event);
|
|
const artistsEvents = await seedTable(
|
|
databases.get(vendor)!,
|
|
1,
|
|
'artists_events',
|
|
{
|
|
artists_id: artist.id,
|
|
events_id: event.id,
|
|
},
|
|
{
|
|
select: ['id'],
|
|
where: ['artists_id', artist.id],
|
|
}
|
|
);
|
|
|
|
expect(artistsEvents).toBeDefined();
|
|
expect(artistsEvents.length).toEqual(1);
|
|
expect(artistsEvents[0].id).toEqual(expect.any(Number));
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/graphql`)
|
|
.set('Authorization', 'Bearer AdminToken')
|
|
.set('Content-Type', 'application/json')
|
|
.send({
|
|
query: `query test {
|
|
artists {
|
|
events {
|
|
artists_id {
|
|
events {
|
|
artists_id (filter: { events: { id: { _eq: ${artistsEvents[0].id} } } }) {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
})
|
|
.expect('Content-Type', /application\/json/)
|
|
.expect(200);
|
|
|
|
expect(response.body.errors).toBeDefined();
|
|
expect(response.body.errors.length).toEqual(1);
|
|
});
|
|
});
|
|
|
|
describe('allow queries up to sort depth limit', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
const artist = createArtist();
|
|
const event = createEvent();
|
|
await seedTable(databases.get(vendor)!, 1, 'artists', artist);
|
|
await seedTable(databases.get(vendor)!, 1, 'events', event);
|
|
await seedTable(databases.get(vendor)!, 1, 'artists_events', {
|
|
artists_id: artist.id,
|
|
events_id: event.id,
|
|
});
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/graphql`)
|
|
.set('Authorization', 'Bearer AdminToken')
|
|
.set('Content-Type', 'application/json')
|
|
.send({
|
|
query: `query test {
|
|
artists (filter: {
|
|
id: { _eq: "${artist.id}" }
|
|
}) {
|
|
events {
|
|
artists_id {
|
|
events {
|
|
artists_id (sort: "id") {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
})
|
|
.expect('Content-Type', /application\/json/)
|
|
.expect(200);
|
|
|
|
expect(response.body.data).toEqual(
|
|
expect.objectContaining({
|
|
artists: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
events: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
artists_id: expect.objectContaining({
|
|
events: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
artists_id: expect.objectContaining({
|
|
id: expect.any(String),
|
|
}),
|
|
}),
|
|
]),
|
|
}),
|
|
}),
|
|
]),
|
|
}),
|
|
]),
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('deny queries over the sort depth limit', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
const artist = createArtist();
|
|
const event = createEvent();
|
|
await seedTable(databases.get(vendor)!, 1, 'artists', artist);
|
|
await seedTable(databases.get(vendor)!, 1, 'events', event);
|
|
const artistsEvents = await seedTable(
|
|
databases.get(vendor)!,
|
|
1,
|
|
'artists_events',
|
|
{
|
|
artists_id: artist.id,
|
|
events_id: event.id,
|
|
},
|
|
{
|
|
select: ['id'],
|
|
where: ['artists_id', artist.id],
|
|
}
|
|
);
|
|
|
|
expect(artistsEvents).toBeDefined();
|
|
expect(artistsEvents.length).toEqual(1);
|
|
expect(artistsEvents[0].id).toEqual(expect.any(Number));
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/graphql`)
|
|
.set('Authorization', 'Bearer AdminToken')
|
|
.set('Content-Type', 'application/json')
|
|
.send({
|
|
query: `query test {
|
|
artists (filter: {
|
|
id: { _eq: "${artist.id}" }
|
|
}) {
|
|
events {
|
|
artists_id {
|
|
events {
|
|
artists_id (sort: "events.id") {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
})
|
|
.expect('Content-Type', /application\/json/)
|
|
.expect(200);
|
|
|
|
expect(response.body.errors).toBeDefined();
|
|
expect(response.body.errors.length).toEqual(1);
|
|
});
|
|
});
|
|
});
|
|
});
|