directus
1import type { Field, Relation } from '@directus/types';
2import { expect, test, describe } from 'vitest';
3import type { Collection } from '../types/index.js';
4import { sanitizeCollection, sanitizeField, sanitizeRelation } from './sanitize-schema.js';
5
6describe('sanitizeCollection', () => {
7test.each([
8// Not supported in SQLite + comment in MSSQL
9{
10collection: 'test',
11meta: {
12accountability: 'all',
13collection: 'test',
14group: null,
15hidden: false,
16icon: null,
17item_duplication_fields: null,
18note: null,
19singleton: false,
20translations: {},
21},
22schema: { comment: null, name: 'test', schema: 'public' },
23},
24// MySQL Only
25{
26collection: 'test',
27meta: {
28accountability: 'all',
29collection: 'test',
30group: null,
31hidden: false,
32icon: null,
33item_duplication_fields: null,
34note: null,
35singleton: false,
36translations: {},
37},
38schema: { collation: 'latin1_swedish_ci', name: 'test', engine: 'InnoDB' },
39},
40// Postgres Only
41{
42collection: 'test',
43meta: {
44accountability: 'all',
45collection: 'test',
46group: null,
47hidden: false,
48icon: null,
49item_duplication_fields: null,
50note: null,
51singleton: false,
52translations: {},
53},
54schema: { name: 'test', owner: 'postgres' },
55},
56// SQLite Only
57{
58collection: 'test',
59meta: {
60accountability: 'all',
61collection: 'test',
62group: null,
63hidden: false,
64icon: null,
65item_duplication_fields: null,
66note: null,
67singleton: false,
68translations: {},
69},
70schema: { name: 'test', sql: 'CREATE TABLE `test` (`id` integer not null primary key autoincrement)' },
71},
72// MSSQL only
73{
74collection: 'test',
75meta: {
76accountability: 'all',
77collection: 'test',
78group: null,
79hidden: false,
80icon: null,
81item_duplication_fields: null,
82note: null,
83singleton: false,
84translations: {},
85},
86schema: { name: 'test', catalog: 'test-db' },
87},
88] satisfies Collection[])('should only contain name property in collection schema', (testCollection) => {
89const result = sanitizeCollection(testCollection);
90
91expect(result).toEqual({
92collection: 'test',
93meta: {
94accountability: 'all',
95collection: 'test',
96group: null,
97hidden: false,
98icon: null,
99item_duplication_fields: null,
100note: null,
101singleton: false,
102translations: {},
103},
104schema: { name: 'test' },
105});
106});
107});
108
109describe('sanitizeField', () => {
110test('should only contain certain properties in field schema when sanitizeAllSchema is false', () => {
111const testField = {
112collection: 'test',
113field: 'id',
114name: 'id',
115meta: {
116id: 1,
117collection: 'test',
118conditions: null,
119display: null,
120display_options: null,
121field: 'id',
122group: null,
123hidden: true,
124interface: 'input',
125note: null,
126options: null,
127readonly: true,
128required: false,
129sort: null,
130special: null,
131translations: null,
132validation: null,
133validation_message: null,
134width: 'full',
135},
136schema: {
137comment: null,
138data_type: 'integer',
139default_value: "nextval('test_id_seq'::regclass)",
140foreign_key_column: null,
141foreign_key_schema: null,
142foreign_key_table: null,
143generation_expression: null,
144has_auto_increment: true,
145is_generated: false,
146is_nullable: false,
147is_primary_key: true,
148is_unique: true,
149max_length: null,
150name: 'id',
151numeric_precision: 32,
152numeric_scale: 0,
153schema: 'public',
154table: 'test',
155},
156type: 'integer',
157} satisfies Field;
158
159const result = sanitizeField(testField);
160
161expect(result).toEqual({
162collection: 'test',
163field: 'id',
164name: 'id',
165meta: {
166id: 1,
167collection: 'test',
168conditions: null,
169display: null,
170display_options: null,
171field: 'id',
172group: null,
173hidden: true,
174interface: 'input',
175note: null,
176options: null,
177readonly: true,
178required: false,
179sort: null,
180special: null,
181translations: null,
182validation: null,
183validation_message: null,
184width: 'full',
185},
186schema: {
187data_type: 'integer',
188default_value: "nextval('test_id_seq'::regclass)",
189foreign_key_column: null,
190foreign_key_table: null,
191generation_expression: null,
192has_auto_increment: true,
193is_generated: false,
194is_nullable: false,
195is_primary_key: true,
196is_unique: true,
197max_length: null,
198name: 'id',
199numeric_precision: 32,
200numeric_scale: 0,
201
202table: 'test',
203},
204type: 'integer',
205});
206});
207
208test('should not contain field schema when sanitizeAllSchema is true', () => {
209const testField = {
210collection: 'test',
211field: 'id',
212name: 'id',
213meta: {
214id: 1,
215collection: 'test',
216conditions: null,
217display: null,
218display_options: null,
219field: 'id',
220group: null,
221hidden: true,
222interface: 'input',
223note: null,
224options: null,
225readonly: true,
226required: false,
227sort: null,
228special: null,
229translations: null,
230validation: null,
231validation_message: null,
232width: 'full',
233},
234schema: {
235data_type: 'integer',
236default_value: "nextval('test_id_seq'::regclass)",
237foreign_key_column: null,
238foreign_key_table: null,
239generation_expression: null,
240has_auto_increment: true,
241is_generated: false,
242is_nullable: false,
243is_primary_key: true,
244is_unique: true,
245max_length: null,
246name: 'id',
247numeric_precision: 32,
248numeric_scale: 0,
249table: 'test',
250},
251type: 'integer',
252} satisfies Field;
253
254const result = sanitizeField(testField, true);
255
256expect(result).toEqual({
257collection: 'test',
258field: 'id',
259name: 'id',
260meta: {
261id: 1,
262collection: 'test',
263conditions: null,
264display: null,
265display_options: null,
266field: 'id',
267group: null,
268hidden: true,
269interface: 'input',
270note: null,
271options: null,
272readonly: true,
273required: false,
274sort: null,
275special: null,
276translations: null,
277validation: null,
278validation_message: null,
279width: 'full',
280},
281type: 'integer',
282});
283});
284});
285
286describe('sanitizeRelation', () => {
287test.each([
288// Postgres + MSSSQL
289{
290collection: 'test_example',
291field: 'm2m',
292related_collection: 'test',
293meta: {
294id: 1,
295junction_field: 'example_id',
296many_collection: 'test_example',
297many_field: 'test_id',
298one_allowed_collections: null,
299one_collection: 'test',
300one_collection_field: null,
301one_deselect_action: 'nullify',
302one_field: 'm2m',
303sort_field: null,
304},
305schema: {
306table: 'test_example',
307column: 'test_id',
308foreign_key_table: 'test',
309foreign_key_column: 'id',
310foreign_key_schema: 'public',
311constraint_name: 'test_example_test_id_foreign',
312on_update: 'NO ACTION',
313on_delete: 'SET NULL',
314},
315},
316] satisfies Relation[])('should only contain certain properties in relation schema', (testRelation) => {
317const result = sanitizeRelation(testRelation);
318
319expect(result).toEqual({
320collection: 'test_example',
321field: 'm2m',
322related_collection: 'test',
323meta: {
324id: 1,
325junction_field: 'example_id',
326many_collection: 'test_example',
327many_field: 'test_id',
328one_allowed_collections: null,
329one_collection: 'test',
330one_collection_field: null,
331one_deselect_action: 'nullify',
332one_field: 'm2m',
333sort_field: null,
334},
335schema: {
336table: 'test_example',
337column: 'test_id',
338foreign_key_table: 'test',
339foreign_key_column: 'id',
340constraint_name: 'test_example_test_id_foreign',
341on_update: 'NO ACTION',
342on_delete: 'SET NULL',
343},
344});
345});
346});
347