mirror of
https://github.com/directus/directus.git
synced 2026-01-31 15:57:57 -05:00
* Expose schema endpoints * respond with 204 * fix payload for export flag * allow export as YAML * use SchemaService * move getStringFromStream to utils * clearer exceptions with logs * check empty request body * specs * add bypassCache flag and remove flushCaches calls * Identify integer typed fields in Oracle * Fix uuid foreign keys type for Oracle * Detect autoincrement for CockroachDB * Bypass cache for fields and relations services * Add same DB tests * Test if working for all vendors * Add schema snapshot testing into sequential flow * Fix schema re-apply attempts when it's a different database vendor (#14816) * prevent diffing on certain properties * fix apply-snapshot test * prevent updates to autoincrement fields' schema * sanitize relations * remove `colleciton.schema.sql` parsing in test It is now being sanitized by `sanitizeCollection()` function in get-snapshot-diff.ts * Change concurrency group to not cancel test running on main * remove multipart for apply & add hash checking * check incoming version & add force query param * refine error message & stub for nested hash * add `vendor` top-level property as an additional safety net for cross db vendor applies * sanitize generated schema snapshots * snapshot joi validation * diff joi validation * minor cleanup * extract applyDiff & use deep-diff applyChange * use applyDiff in schema service * Mark vendor as optional * Update tests to apply diffs * move force flag check into service * Patch mssql vendor typo * Set relation no action in cyclic relations * Update mysql timestamp default value * Oracle cannot define no action * Update oracle timestamp type default values * add hash checking for outdated diffs * fix diff service & endpoint * Add hashes when returning after * Fix self referencing relations for Oracle * Add temp fix for CURRENT_TIMESTAMP defaults in Oracle * clean up driver and database client types * only require diff hash if not kind N * update hash comparison logic for create/delete * Set no action constraint for self referencing M2M in MSSQL * Add basic hash check tests * omit default value when hashing auto increments Specifically for CockroachDB with differing sequences * add vendor check * update specs * Validate vendors with type definition * Spread the vendors input array * re-add Export component * re-add js-yaml to root * Propagate mutation options for schema apply * Verify that snapshots differ and clear cache before hash tests * Fix unit test * Revert temp fix for CURRENT_TIMESTAMP defaults in Oracle * Define and reuse type for export format * Define and reuse list of database clients * change `were` to `was` * change `where` to `were` * add some empty lines for readability * tweak exception message * fix test * use object-hash * use boolean to check whether file is included * simplify request content type check * throw error when multiple files were uploaded * use nullish coalesce instead of short circuit * Update api/src/services/schema.ts Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com> * Remove unnecessary `Omit` on `SnapshotWithHash` type * Revert "Remove unnecessary `Omit` on `SnapshotWithHash` type" This reverts commitd22ac771ec. * check empty snapshot earlier * use allow-list logic via pick instead of omit * Update api/src/services/schema.ts Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch> * Move to own /schema endpoint controller * Fix refs to schema endpoint * move streamToString to utils package * move get-versioned-hash and add test * extract kind into an enum * Fix mysql5 timestamp value * Fix test collection existing on rerun * resolve TODO comment in blackbox test * Drop deep level hashes in diff These hashes are used only for more accurate error reporting but are otherwise superfluous, since changes are already detected by the top level hash. Therefore we remove them in favor of a simpler diff format and implementation. * Revert schema "fix" for createItem, add comment * Strict diff schema validation * Revert CrDB auto-increment detection patch in816c998* Clear systemCache to expose newly added fields * Use DiffKind constants * Extract diff & snapshot validation into own tested utils * Apply suggestions from @azrikahar * Update knex-schema-inspector to 3.0.1 Includes the fix for CrDB auto-increment detection (knex/knex-schema-inspector#135) * Update knex-schema-inspector in packages * Update lock file * add test for schema service * add test for export service * add relevant tests to modified util functions * fix csv test to account for os end of line * fix files controller test * dedupe test data for schema service * Align schema specs with docs * Update api/src/controllers/schema.ts * Revert testing for all vendors --------- Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com> Co-authored-by: ian <licitdev@gmail.com> Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
710 lines
18 KiB
TypeScript
710 lines
18 KiB
TypeScript
import request from 'supertest';
|
|
import { getUrl } from './config';
|
|
import * as common from './index';
|
|
import vendors from './get-dbs-to-test';
|
|
import { Filter } from '@directus/shared/types';
|
|
|
|
export function DisableTestCachingSetup() {
|
|
beforeEach(async () => {
|
|
process.env.TEST_NO_CACHE = 'true';
|
|
});
|
|
|
|
afterAll(async () => {
|
|
delete process.env.TEST_NO_CACHE;
|
|
});
|
|
}
|
|
|
|
export function ClearCaches() {
|
|
describe('Clear Caches', () => {
|
|
it.each(vendors)(
|
|
'%s',
|
|
async (vendor) => {
|
|
// Setup
|
|
common.EnableTestCaching();
|
|
|
|
// Assert
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/utils/cache/clear`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`);
|
|
|
|
const response2 = await request(getUrl(vendor))
|
|
.get(`/fields`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`);
|
|
|
|
expect(response.statusCode).toBe(200);
|
|
expect(response2.statusCode).toBe(200);
|
|
},
|
|
30000
|
|
);
|
|
});
|
|
}
|
|
|
|
export function EnableTestCaching() {
|
|
delete process.env.TEST_NO_CACHE;
|
|
}
|
|
|
|
export type OptionsCreateRole = {
|
|
name: string;
|
|
appAccessEnabled: boolean;
|
|
adminAccessEnabled: boolean;
|
|
};
|
|
|
|
export async function CreateRole(vendor: string, options: OptionsCreateRole) {
|
|
// Action
|
|
const roleResponse = await request(getUrl(vendor))
|
|
.get(`/roles`)
|
|
.query({
|
|
filter: { name: { _eq: options.name } },
|
|
})
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`);
|
|
|
|
if (roleResponse.body.data.length > 0) {
|
|
return roleResponse.body.data[0];
|
|
}
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/roles`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`)
|
|
.send({ name: options.name, app_access: options.appAccessEnabled, admin_access: options.adminAccessEnabled });
|
|
|
|
return response.body.data;
|
|
}
|
|
|
|
export type OptionsCreateUser = {
|
|
token: string;
|
|
email: string;
|
|
password?: string;
|
|
name?: string;
|
|
role?: string;
|
|
// Automatically removed params
|
|
roleName?: string; // to generate role
|
|
};
|
|
|
|
export async function CreateUser(vendor: string, options: Partial<OptionsCreateUser>) {
|
|
// Validate options
|
|
if (!options.token) {
|
|
throw new Error('Missing required field: token');
|
|
}
|
|
if (!options.email) {
|
|
throw new Error('Missing required field: email');
|
|
}
|
|
|
|
if (options.roleName) {
|
|
const roleResponse = await request(getUrl(vendor))
|
|
.get(`/roles`)
|
|
.query({
|
|
filter: { name: { _eq: options.roleName } },
|
|
fields: ['id', 'name'],
|
|
})
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`);
|
|
|
|
if (roleResponse.body.data.length === 0) {
|
|
throw new Error(`Role ${options.roleName} does not exist`);
|
|
}
|
|
|
|
options.role = roleResponse.body.data[0].id;
|
|
delete options.roleName;
|
|
}
|
|
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/users`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`)
|
|
.send(options);
|
|
|
|
return response.body.data;
|
|
}
|
|
|
|
export type OptionsCreateCollection = {
|
|
collection: string;
|
|
meta?: any;
|
|
schema?: any;
|
|
fields?: any;
|
|
// Automatically removed params
|
|
primaryKeyType?: common.PrimaryKeyType;
|
|
};
|
|
|
|
export async function CreateCollection(vendor: string, options: Partial<OptionsCreateCollection>) {
|
|
// Validate options
|
|
if (!options.collection) {
|
|
throw new Error('Missing required field: collection');
|
|
}
|
|
|
|
// Parse options
|
|
const defaultOptions = {
|
|
meta: {},
|
|
schema: {},
|
|
fields: [],
|
|
primaryKeyType: 'integer',
|
|
};
|
|
|
|
options = Object.assign({}, defaultOptions, options);
|
|
|
|
switch (options.primaryKeyType) {
|
|
case 'uuid':
|
|
options.fields.push({
|
|
field: 'id',
|
|
type: 'uuid',
|
|
meta: { hidden: true, readonly: true, interface: 'input', special: ['uuid'] },
|
|
schema: { is_primary_key: true, length: 36, has_auto_increment: false },
|
|
});
|
|
break;
|
|
case 'string':
|
|
options.fields.push({
|
|
field: 'id',
|
|
type: 'string',
|
|
meta: { hidden: false, readonly: false, interface: 'input' },
|
|
schema: { is_primary_key: true, length: 255, has_auto_increment: false },
|
|
});
|
|
break;
|
|
case 'integer':
|
|
default:
|
|
options.fields.push({
|
|
field: 'id',
|
|
type: 'integer',
|
|
meta: { hidden: true, interface: 'input', readonly: true },
|
|
schema: { is_primary_key: true, has_auto_increment: true },
|
|
});
|
|
break;
|
|
}
|
|
|
|
if (options.primaryKeyType) {
|
|
delete options.primaryKeyType;
|
|
}
|
|
|
|
// Action
|
|
const collectionResponse = await request(getUrl(vendor))
|
|
.get(`/collections/${options.collection}`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`);
|
|
|
|
if (collectionResponse.body.data) {
|
|
return collectionResponse.body.data;
|
|
}
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/collections`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`)
|
|
.send(options);
|
|
|
|
return response.body.data;
|
|
}
|
|
|
|
export type OptionsDeleteCollection = {
|
|
collection: string;
|
|
};
|
|
|
|
export async function DeleteCollection(vendor: string, options: OptionsDeleteCollection) {
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.delete(`/collections/${options.collection}`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`);
|
|
|
|
return response.body;
|
|
}
|
|
|
|
export type OptionsDeleteField = {
|
|
collection: string;
|
|
field: string;
|
|
};
|
|
|
|
export async function DeleteField(vendor: string, options: OptionsDeleteField) {
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.delete(`/fields/${options.collection}/${options.field}`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`);
|
|
|
|
return response.body;
|
|
}
|
|
|
|
export type OptionsCreateField = {
|
|
collection: string;
|
|
field: string;
|
|
type: string;
|
|
meta?: any;
|
|
schema?: any;
|
|
};
|
|
|
|
export async function CreateField(vendor: string, options: OptionsCreateField) {
|
|
// Parse options
|
|
const defaultOptions = {
|
|
meta: {},
|
|
schema: {},
|
|
};
|
|
|
|
options = Object.assign({}, defaultOptions, options);
|
|
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/fields/${options.collection}`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`)
|
|
.send(options);
|
|
|
|
return response.body.data;
|
|
}
|
|
|
|
export type OptionsCreateRelation = {
|
|
collection: string;
|
|
field: string;
|
|
related_collection: string | null;
|
|
meta?: any;
|
|
schema?: any;
|
|
};
|
|
|
|
export async function CreateRelation(vendor: string, options: OptionsCreateRelation) {
|
|
// Parse options
|
|
const defaultOptions = {
|
|
meta: {},
|
|
schema: {},
|
|
};
|
|
|
|
options = Object.assign({}, defaultOptions, options);
|
|
|
|
// Action
|
|
const relationResponse = await request(getUrl(vendor))
|
|
.get(`/relations/${options.collection}/${options.field}`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`);
|
|
|
|
if (relationResponse.statusCode === 200) {
|
|
return relationResponse.body.data;
|
|
}
|
|
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/relations`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`)
|
|
.send(options);
|
|
|
|
return response.body.data;
|
|
}
|
|
|
|
export type OptionsCreateFieldM2O = {
|
|
collection: string;
|
|
field: string;
|
|
fieldMeta?: any;
|
|
fieldSchema?: any;
|
|
primaryKeyType?: common.PrimaryKeyType;
|
|
otherCollection: string;
|
|
relationMeta?: any;
|
|
relationSchema?: any;
|
|
};
|
|
|
|
export async function CreateFieldM2O(vendor: string, options: OptionsCreateFieldM2O) {
|
|
// Parse options
|
|
const defaultOptions = {
|
|
fieldMeta: {},
|
|
fieldSchema: {},
|
|
primaryKeyType: 'integer',
|
|
relationMeta: {},
|
|
relationSchema: {
|
|
on_delete: 'SET NULL',
|
|
},
|
|
};
|
|
|
|
options = Object.assign({}, defaultOptions, options);
|
|
|
|
const fieldOptions: OptionsCreateField = {
|
|
collection: options.collection,
|
|
field: options.field,
|
|
type: options.primaryKeyType!,
|
|
meta: options.fieldMeta ?? {},
|
|
schema: options.fieldSchema ?? {},
|
|
};
|
|
|
|
if (!fieldOptions.meta.special) {
|
|
fieldOptions.meta.special = ['m2o'];
|
|
} else if (!fieldOptions.meta.special.includes('m2o')) {
|
|
fieldOptions.meta.special.push('m2o');
|
|
}
|
|
|
|
// Action
|
|
const field = await CreateField(vendor, fieldOptions);
|
|
|
|
const relationOptions: OptionsCreateRelation = {
|
|
collection: options.collection,
|
|
field: options.field,
|
|
meta: options.relationMeta,
|
|
schema: options.relationSchema,
|
|
related_collection: options.otherCollection,
|
|
};
|
|
|
|
const relation = await CreateRelation(vendor, relationOptions);
|
|
|
|
return { field, relation };
|
|
}
|
|
|
|
export type OptionsCreateFieldO2M = {
|
|
collection: string;
|
|
field: string;
|
|
fieldMeta?: any;
|
|
otherCollection: string;
|
|
otherField: string;
|
|
primaryKeyType?: string;
|
|
otherMeta?: any;
|
|
otherSchema?: any;
|
|
relationMeta?: any;
|
|
relationSchema?: any;
|
|
};
|
|
|
|
export async function CreateFieldO2M(vendor: string, options: OptionsCreateFieldO2M) {
|
|
// Parse options
|
|
const defaultOptions = {
|
|
fieldMeta: {},
|
|
primaryKeyType: 'integer',
|
|
otherMeta: {},
|
|
otherSchema: {},
|
|
relationMeta: {},
|
|
relationSchema: {
|
|
on_delete: 'SET NULL',
|
|
},
|
|
};
|
|
|
|
options = Object.assign({}, defaultOptions, options);
|
|
|
|
const fieldOptions: OptionsCreateField = {
|
|
collection: options.collection,
|
|
field: options.field,
|
|
type: 'alias',
|
|
meta: options.fieldMeta,
|
|
schema: null,
|
|
};
|
|
|
|
if (!fieldOptions.meta.special) {
|
|
fieldOptions.meta.special = ['o2m'];
|
|
} else if (!fieldOptions.meta.special.includes('o2m')) {
|
|
fieldOptions.meta.special.push('o2m');
|
|
}
|
|
|
|
// Action
|
|
const field = await CreateField(vendor, fieldOptions);
|
|
|
|
const otherFieldOptions: OptionsCreateField = {
|
|
collection: options.otherCollection,
|
|
field: options.otherField,
|
|
type: options.primaryKeyType!,
|
|
meta: options.otherMeta,
|
|
schema: options.otherSchema,
|
|
};
|
|
|
|
const otherField = await CreateField(vendor, otherFieldOptions);
|
|
|
|
const relationOptions: OptionsCreateRelation = {
|
|
collection: options.otherCollection,
|
|
field: options.otherField,
|
|
meta: { ...options.relationMeta, one_field: options.field },
|
|
schema: options.relationSchema,
|
|
related_collection: options.collection,
|
|
};
|
|
|
|
const relation = await CreateRelation(vendor, relationOptions);
|
|
|
|
return { field, otherField, relation };
|
|
}
|
|
|
|
export type OptionsCreateFieldM2M = {
|
|
collection: string;
|
|
field: string;
|
|
fieldMeta?: any;
|
|
fieldSchema?: any;
|
|
otherCollection: string;
|
|
otherField: string;
|
|
junctionCollection: string;
|
|
primaryKeyType?: string;
|
|
otherMeta?: any;
|
|
otherSchema?: any;
|
|
relationMeta?: any;
|
|
relationSchema?: any;
|
|
otherRelationSchema?: any;
|
|
};
|
|
|
|
export async function CreateFieldM2M(vendor: string, options: OptionsCreateFieldM2M) {
|
|
// Parse options
|
|
const defaultOptions = {
|
|
fieldMeta: {},
|
|
fieldSchema: {},
|
|
primaryKeyType: 'integer',
|
|
otherMeta: {},
|
|
otherSchema: {},
|
|
relationMeta: {},
|
|
relationSchema: {
|
|
on_delete: 'SET NULL',
|
|
},
|
|
otherRelationSchema: {
|
|
on_delete: 'SET NULL',
|
|
},
|
|
};
|
|
|
|
options = Object.assign({}, defaultOptions, options);
|
|
|
|
const fieldOptions: OptionsCreateField = {
|
|
collection: options.collection,
|
|
field: options.field,
|
|
type: 'alias',
|
|
meta: options.fieldMeta,
|
|
schema: options.fieldSchema,
|
|
};
|
|
|
|
const isSelfReferencing = options.collection === options.otherCollection;
|
|
|
|
if (!fieldOptions.meta.special) {
|
|
fieldOptions.meta.special = ['m2m'];
|
|
} else if (!fieldOptions.meta.special.includes('m2m')) {
|
|
fieldOptions.meta.special.push('m2m');
|
|
}
|
|
|
|
// Action
|
|
const field = await CreateField(vendor, fieldOptions);
|
|
|
|
const otherFieldOptions: OptionsCreateField = {
|
|
collection: options.otherCollection,
|
|
field: options.otherField,
|
|
type: 'alias',
|
|
meta: options.otherMeta,
|
|
schema: options.otherSchema,
|
|
};
|
|
|
|
if (!otherFieldOptions.meta.special) {
|
|
otherFieldOptions.meta.special = ['m2m'];
|
|
} else if (!otherFieldOptions.meta.special.includes('m2m')) {
|
|
otherFieldOptions.meta.special.push('m2m');
|
|
}
|
|
|
|
const otherField = await CreateField(vendor, otherFieldOptions);
|
|
|
|
const junctionCollectionOptions: OptionsCreateCollection = {
|
|
collection: options.junctionCollection,
|
|
primaryKeyType: 'integer',
|
|
};
|
|
|
|
const junctionCollection = await CreateCollection(vendor, junctionCollectionOptions);
|
|
|
|
const junctionFieldName = `${options.collection}_id`;
|
|
const junctionFieldOptions: OptionsCreateField = {
|
|
collection: options.junctionCollection,
|
|
field: junctionFieldName,
|
|
type: options.primaryKeyType!,
|
|
};
|
|
|
|
const junctionField = await CreateField(vendor, junctionFieldOptions);
|
|
|
|
const otherJunctionFieldName = `${options.otherCollection}_id${isSelfReferencing ? '2' : ''}`;
|
|
const otherJunctionFieldOptions: OptionsCreateField = {
|
|
collection: options.junctionCollection,
|
|
field: otherJunctionFieldName,
|
|
type: options.primaryKeyType!,
|
|
};
|
|
|
|
const otherJunctionField = await CreateField(vendor, otherJunctionFieldOptions);
|
|
|
|
const relationOptions: OptionsCreateRelation = {
|
|
collection: options.junctionCollection,
|
|
field: junctionFieldName,
|
|
meta: {
|
|
...options.relationMeta,
|
|
one_field: options.field,
|
|
junction_field: otherJunctionFieldName,
|
|
},
|
|
schema: options.relationSchema,
|
|
related_collection: options.collection,
|
|
};
|
|
|
|
const relation = await CreateRelation(vendor, relationOptions);
|
|
|
|
const otherRelationOptions: OptionsCreateRelation = {
|
|
collection: options.junctionCollection,
|
|
field: otherJunctionFieldName,
|
|
meta: {
|
|
...options.relationMeta,
|
|
one_field: options.otherField,
|
|
junction_field: junctionFieldName,
|
|
},
|
|
schema: options.otherRelationSchema,
|
|
related_collection: options.otherCollection,
|
|
};
|
|
|
|
const otherRelation = await CreateRelation(vendor, otherRelationOptions);
|
|
|
|
return { field, otherField, junctionCollection, junctionField, otherJunctionField, relation, otherRelation };
|
|
}
|
|
|
|
export type OptionsCreateFieldM2A = {
|
|
collection: string;
|
|
field: string;
|
|
relatedCollections: string[];
|
|
fieldMeta?: any;
|
|
fieldSchema?: any;
|
|
junctionCollection: string;
|
|
primaryKeyType?: string;
|
|
relationMeta?: any;
|
|
relationSchema?: any;
|
|
itemRelationMeta?: any;
|
|
itemRelationSchema?: any;
|
|
};
|
|
|
|
export async function CreateFieldM2A(vendor: string, options: OptionsCreateFieldM2A) {
|
|
// Parse options
|
|
const defaultOptions = {
|
|
fieldMeta: {},
|
|
fieldSchema: {},
|
|
primaryKeyType: 'integer',
|
|
otherMeta: {},
|
|
otherSchema: {},
|
|
relationSchema: null,
|
|
otherRelationSchema: {
|
|
on_delete: 'SET NULL',
|
|
},
|
|
};
|
|
|
|
options = Object.assign({}, defaultOptions, options);
|
|
|
|
const fieldOptions: OptionsCreateField = {
|
|
collection: options.collection,
|
|
field: options.field,
|
|
type: 'alias',
|
|
meta: options.fieldMeta,
|
|
schema: options.fieldSchema,
|
|
};
|
|
|
|
if (!fieldOptions.meta.special) {
|
|
fieldOptions.meta.special = ['m2a'];
|
|
} else if (!fieldOptions.meta.special.includes('m2a')) {
|
|
fieldOptions.meta.special.push('m2a');
|
|
}
|
|
|
|
// Action
|
|
const field = await CreateField(vendor, fieldOptions);
|
|
|
|
const junctionCollectionOptions: OptionsCreateCollection = {
|
|
collection: options.junctionCollection,
|
|
primaryKeyType: 'integer',
|
|
};
|
|
|
|
const junctionCollection = await CreateCollection(vendor, junctionCollectionOptions);
|
|
|
|
const junctionFieldName = `${options.junctionCollection}_id`;
|
|
const junctionFieldOptions: OptionsCreateField = {
|
|
collection: options.junctionCollection,
|
|
field: junctionFieldName,
|
|
type: options.primaryKeyType!,
|
|
meta: { hidden: true },
|
|
};
|
|
|
|
const junctionField = await CreateField(vendor, junctionFieldOptions);
|
|
|
|
const junctionFieldItemOptions: OptionsCreateField = {
|
|
collection: options.junctionCollection,
|
|
field: 'item',
|
|
type: 'string',
|
|
meta: { hidden: true },
|
|
};
|
|
|
|
const junctionFieldItem = await CreateField(vendor, junctionFieldItemOptions);
|
|
|
|
const junctionFieldCollectionOptions: OptionsCreateField = {
|
|
collection: options.junctionCollection,
|
|
field: 'collection',
|
|
type: 'string',
|
|
meta: { hidden: true },
|
|
};
|
|
|
|
const junctionFieldCollection = await CreateField(vendor, junctionFieldCollectionOptions);
|
|
|
|
const relationOptions: OptionsCreateRelation = {
|
|
collection: options.junctionCollection,
|
|
field: 'item',
|
|
related_collection: null,
|
|
meta: {
|
|
one_allowed_collections: options.relatedCollections,
|
|
one_collection_field: 'collection',
|
|
junction_field: junctionFieldName,
|
|
},
|
|
schema: null,
|
|
};
|
|
|
|
const relation = await CreateRelation(vendor, relationOptions);
|
|
|
|
const itemRelationOptions: OptionsCreateRelation = {
|
|
collection: options.junctionCollection,
|
|
field: junctionFieldName,
|
|
related_collection: options.collection,
|
|
meta: {
|
|
one_field: options.field,
|
|
junction_field: 'item',
|
|
},
|
|
schema: options.itemRelationSchema,
|
|
};
|
|
|
|
const itemRelation = await CreateRelation(vendor, itemRelationOptions);
|
|
|
|
return {
|
|
field,
|
|
junctionCollection,
|
|
junctionField,
|
|
junctionFieldItem,
|
|
junctionFieldCollection,
|
|
relation,
|
|
otherRelation: itemRelation,
|
|
};
|
|
}
|
|
|
|
export type OptionsCreateItem = {
|
|
collection: string;
|
|
item: any;
|
|
};
|
|
|
|
export async function CreateItem(vendor: string, options: OptionsCreateItem) {
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.post(`/items/${options.collection}`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`)
|
|
.send(options.item);
|
|
|
|
return response.body.data;
|
|
}
|
|
|
|
export type OptionsReadItem = {
|
|
collection: string;
|
|
filter?: Filter;
|
|
fields?: string;
|
|
};
|
|
|
|
export async function ReadItem(vendor: string, options: OptionsReadItem) {
|
|
// Parse options
|
|
const defaultOptions = {
|
|
filter: {},
|
|
fields: '*',
|
|
};
|
|
|
|
options = Object.assign({}, defaultOptions, options);
|
|
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.get(`/items/${options.collection}`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`)
|
|
.query({
|
|
filter: options.filter,
|
|
fields: options.fields,
|
|
});
|
|
|
|
return response.body.data;
|
|
}
|
|
|
|
export type OptionsUpdateItem = {
|
|
id?: string | number;
|
|
collection: string;
|
|
item: any;
|
|
};
|
|
|
|
export async function UpdateItem(vendor: string, options: OptionsUpdateItem) {
|
|
// Action
|
|
const response = await request(getUrl(vendor))
|
|
.patch(`/items/${options.collection}/${options.id === undefined ? '' : options.id}`)
|
|
.set('Authorization', `Bearer ${common.USER.TESTS_FLOW.TOKEN}`)
|
|
.send(options.item);
|
|
|
|
return response.body.data;
|
|
}
|
|
|
|
// TODO
|
|
// export async function CreatePermissions() {}
|
|
// export async function UpdatePermissions() {}
|
|
// export async function DeletePermissions() {}
|