mirror of
https://github.com/directus/directus.git
synced 2026-02-07 05:55:06 -05:00
* Speed query up by reusing existing aliases which reduces table joins * Use subquery in top level m2o to remove duplicates * Fix linting * Apply distinct on primary key field in subqueries * Use distinct instead as there are only primary keys * Apply subquery on top level * Try remove sub sub query * Test if working for all vendors * Add support for _none and _some * Use subquery only when field depth > 1 * Add tests * Use original table names for columns with functions (#14690) * Use original table names for columns with functions * Extract filter function path parsing as shared util * Fix filter function path when adding node * Pass the originalCollectionName into filter functions * Update unit test * Replace functions within deep GraphQL * Fix invalid operator error for _none and _some * Add filter function tests * Revert triggering for all vendors * Simplify aliasMap * Replace functions in filter within GraphQL aggregate query * Add API support for filtering of alias field * Mark schema as optional * Shift logical operators upwards * Separate recursive parseFilter * Rework shifting of logical operators * Error on invalid usage of _none and _some * Use inner join to preserve sort order * Run tests for all vendors * Reuse aliasMap for sort and filter * Sort on top level query * Remove unnecessary limit on wrapper query * Refactor applyQuery options * Remove duplicates from nested multi relational sort * Fix offset in MSSQL requiring OrderBy * Disable schema cache * Use inner query only for nested sort or multi relational filter * Fix MSSQL duplicate order column * Use inner query only for multi relational * Additional integration tests * Order within partition for multi relational sorts * Rename to directus_row_number * Fix unit test * Add base sort and filter tests * Fix Oracle uppercased rowNumber column * Fix unit test * Fix top level query sort with function * Parse functions in inner query * Increase clarity with knex.ref() * Remove sort filter for top level primary key * Fix unit test * Bypass queries with groupBy * Add collection to aliasMap to fix functions in nested sort * Fix multi relational sort with functions * Add tests for filter and sort with functions * Fix accidental deletion of brackets * Fix top level alias filter node interface * Update M2M sort tests * Add M2A tests * Cast m2a primary key as varchar2 for oracle * Enable filtering tests for M2A * Fix prototype polluting assignment in aliasMap * Remove unnecessary currentKey * Simplify code to increase readability Co-authored-by: Brainslug <br41nslug@users.noreply.github.com> * Fix linting and missing 'this' error * Revert optional chaining * Add mysql5 to tests * Fix mysql5 missing rowNumber() * Overcome indexing delays in MySQL5 * Verify MySQL5 sorting is in order as the result count varies between runs * Skip joining when sorting field already exists * Simplify variable assignment Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Fix linting * Reduce duplicate logic with vars * Transform _func fields in GraphQL only for valid functions * Fix unit test * Fix unsupported date_part() in CrDB Co-authored-by: Brainslug <br41nslug@users.noreply.github.com> Co-authored-by: Roger Stringer <roger@directus.io> Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
230 lines
6.4 KiB
TypeScript
230 lines
6.4 KiB
TypeScript
import request from 'supertest';
|
|
import { getUrl } from '@common/config';
|
|
import vendors from '@common/get-dbs-to-test';
|
|
import * as common from '@common/index';
|
|
import { collectionSingleton, collectionSingletonO2M, seedDBValues } from './singleton.seed';
|
|
import { requestGraphQL, SeedFunctions } from '@common/index';
|
|
|
|
let isSeeded = false;
|
|
|
|
beforeAll(async () => {
|
|
isSeeded = await seedDBValues();
|
|
}, 300000);
|
|
|
|
test('Seed Database Values', () => {
|
|
expect(isSeeded).toStrictEqual(true);
|
|
});
|
|
|
|
describe.each(common.PRIMARY_KEY_TYPES)('/items', (pkType) => {
|
|
const localCollectionSingleton = `${collectionSingleton}_${pkType}`;
|
|
const localCollectionSingletonO2M = `${collectionSingletonO2M}_${pkType}`;
|
|
|
|
describe(`pkType: ${pkType}`, () => {
|
|
describe('GET /:collection', () => {
|
|
describe('retrieves singleton', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.get(`/items/${localCollectionSingleton}`)
|
|
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
|
|
|
|
const gqlResponse = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
|
|
query: {
|
|
[localCollectionSingleton]: {
|
|
name: true,
|
|
o2m: {
|
|
id: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
// Assert
|
|
expect(response.statusCode).toEqual(200);
|
|
expect(response.body.data).toMatchObject({ name: 'parent', o2m: expect.anything() });
|
|
|
|
expect(gqlResponse.statusCode).toEqual(200);
|
|
expect(gqlResponse.body.data).toMatchObject({
|
|
[localCollectionSingleton]: { name: 'parent', o2m: expect.anything() },
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Error handling', () => {
|
|
describe('returns an error when an invalid id is used', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.get(`/items/${localCollectionSingleton}/invalid_id`)
|
|
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
|
|
|
|
const gqlResponse = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
|
|
query: {
|
|
[localCollectionSingleton]: {
|
|
__args: {
|
|
filter: {
|
|
id: {
|
|
_eq: 'invalid_id',
|
|
},
|
|
},
|
|
},
|
|
name: true,
|
|
o2m: {
|
|
id: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
// Assert
|
|
expect(response.statusCode).toBe(403);
|
|
expect(gqlResponse.statusCode).toBe(400);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('PATCH /:collection', () => {
|
|
describe(`updates singleton's name with no relations`, () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
// Setup
|
|
const newName = 'parent_updated';
|
|
const newName2 = 'parent_updated2';
|
|
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.patch(`/items/${localCollectionSingleton}`)
|
|
.send({ name: newName })
|
|
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
|
|
|
|
const mutationKey = `update_${localCollectionSingleton}`;
|
|
|
|
const gqlResponse = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
|
|
mutation: {
|
|
[mutationKey]: {
|
|
__args: {
|
|
data: {
|
|
name: newName2,
|
|
},
|
|
},
|
|
name: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
// Assert
|
|
expect(response.statusCode).toEqual(200);
|
|
expect(response.body.data).toMatchObject({
|
|
name: newName,
|
|
});
|
|
|
|
expect(gqlResponse.statusCode).toBe(200);
|
|
expect(gqlResponse.body.data[mutationKey]).toEqual({
|
|
name: newName2,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('updates o2m items', () => {
|
|
it.each(vendors)('%s', async (vendor) => {
|
|
// Setup
|
|
const existingItem = (
|
|
await request(getUrl(vendor))
|
|
.get(`/items/${localCollectionSingleton}`)
|
|
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`)
|
|
).body.data;
|
|
|
|
const o2mNameNew = 'child_o2m_new';
|
|
const o2mNameNew2 = 'child_o2m_new2';
|
|
const o2mNameUpdated = 'child_o2m_updated';
|
|
const o2mNameUpdated2 = 'child_o2m_updated2';
|
|
|
|
const body = {
|
|
o2m: {
|
|
create: [
|
|
{
|
|
id:
|
|
pkType === 'string'
|
|
? SeedFunctions.generatePrimaryKeys(pkType, {
|
|
quantity: 1,
|
|
seed: `${localCollectionSingletonO2M}_update_o2m`,
|
|
})[0]
|
|
: undefined,
|
|
name: o2mNameNew,
|
|
},
|
|
],
|
|
update: [
|
|
{
|
|
id: existingItem.o2m[0],
|
|
name: o2mNameUpdated,
|
|
},
|
|
],
|
|
delete: [],
|
|
},
|
|
};
|
|
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.patch(`/items/${localCollectionSingleton}?fields=*.*`)
|
|
.send(body)
|
|
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
|
|
|
|
const gqlResponsePre = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
|
|
query: {
|
|
[localCollectionSingleton]: {
|
|
o2m: {
|
|
id: true,
|
|
name: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const updatedO2M = gqlResponsePre.body.data[localCollectionSingleton].o2m;
|
|
const newO2mItem: any = { name: o2mNameNew2 };
|
|
if (pkType === 'string') {
|
|
newO2mItem.id = SeedFunctions.generatePrimaryKeys(pkType, {
|
|
quantity: 1,
|
|
seed: `${localCollectionSingletonO2M}_update_o2m2`,
|
|
})[0];
|
|
}
|
|
updatedO2M.push(newO2mItem);
|
|
updatedO2M[0].name = o2mNameUpdated2;
|
|
|
|
const mutationKey = `update_${localCollectionSingleton}`;
|
|
|
|
const gqlResponse = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
|
|
mutation: {
|
|
[mutationKey]: {
|
|
__args: {
|
|
data: {
|
|
o2m: updatedO2M,
|
|
},
|
|
},
|
|
o2m: {
|
|
name: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
// Assert
|
|
expect(response.statusCode).toEqual(200);
|
|
expect(response.body.data.o2m).toBeDefined();
|
|
expect(response.body.data.o2m.length).toBe(2);
|
|
expect(response.body.data.o2m.map((item: any) => item.name)).toEqual(
|
|
expect.arrayContaining([o2mNameNew, o2mNameUpdated])
|
|
);
|
|
|
|
expect(gqlResponse.statusCode).toEqual(200);
|
|
expect(gqlResponse.body.data[mutationKey].o2m).toBeDefined();
|
|
expect(gqlResponse.body.data[mutationKey].o2m.length).toBe(3);
|
|
expect(gqlResponse.body.data[mutationKey].o2m.map((item: any) => item.name)).toEqual(
|
|
expect.arrayContaining([o2mNameNew2, o2mNameUpdated2])
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|