1
import type { Accountability } from '@directus/types';
2
import type { Knex } from 'knex';
3
import getDatabase from '../database/index.js';
4
import { ForbiddenError } from '@directus/errors';
6
AbstractServiceOptions,
11
} from '../types/index.js';
12
import { applyDiff } from '../utils/apply-diff.js';
13
import { getSnapshotDiff } from '../utils/get-snapshot-diff.js';
14
import { getSnapshot } from '../utils/get-snapshot.js';
15
import { getVersionedHash } from '../utils/get-versioned-hash.js';
16
import { validateApplyDiff } from '../utils/validate-diff.js';
17
import { validateSnapshot } from '../utils/validate-snapshot.js';
19
export class SchemaService {
21
accountability: Accountability | null;
23
constructor(options: Omit<AbstractServiceOptions, 'schema'>) {
24
this.knex = options.knex ?? getDatabase();
25
this.accountability = options.accountability ?? null;
28
async snapshot(): Promise<Snapshot> {
29
if (this.accountability?.admin !== true) throw new ForbiddenError();
31
const currentSnapshot = await getSnapshot({ database: this.knex });
33
return currentSnapshot;
36
async apply(payload: SnapshotDiffWithHash): Promise<void> {
37
if (this.accountability?.admin !== true) throw new ForbiddenError();
39
const currentSnapshot = await this.snapshot();
40
const snapshotWithHash = this.getHashedSnapshot(currentSnapshot);
42
if (!validateApplyDiff(payload, snapshotWithHash)) return;
44
await applyDiff(currentSnapshot, payload.diff, { database: this.knex });
49
options?: { currentSnapshot?: Snapshot; force?: boolean },
50
): Promise<SnapshotDiff | null> {
51
if (this.accountability?.admin !== true) throw new ForbiddenError();
53
validateSnapshot(snapshot, options?.force);
55
const currentSnapshot = options?.currentSnapshot ?? (await getSnapshot({ database: this.knex }));
56
const diff = getSnapshotDiff(currentSnapshot, snapshot);
58
if (diff.collections.length === 0 && diff.fields.length === 0 && diff.relations.length === 0) {
65
getHashedSnapshot(snapshot: Snapshot): SnapshotWithHash {
66
const snapshotHash = getVersionedHash(snapshot);