-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
meta.ts
100 lines (76 loc) · 2.93 KB
/
meta.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import type { Accountability, Query, SchemaOverview } from '@directus/types';
import type { Knex } from 'knex';
import getDatabase from '../database/index.js';
import { ForbiddenError } from '@directus/errors';
import type { AbstractServiceOptions } from '../types/index.js';
import { applyFilter, applySearch } from '../utils/apply-query.js';
export class MetaService {
knex: Knex;
accountability: Accountability | null;
schema: SchemaOverview;
constructor(options: AbstractServiceOptions) {
this.knex = options.knex || getDatabase();
this.accountability = options.accountability || null;
this.schema = options.schema;
}
async getMetaForQuery(collection: string, query: any): Promise<Record<string, any> | undefined> {
if (!query || !query.meta) return;
const results = await Promise.all(
query.meta.map((metaVal: string) => {
if (metaVal === 'total_count') return this.totalCount(collection);
if (metaVal === 'filter_count') return this.filterCount(collection, query);
return undefined;
})
);
return results.reduce((metaObject: Record<string, any>, value, index) => {
return {
...metaObject,
[query.meta![index]]: value,
};
}, {});
}
async totalCount(collection: string): Promise<number> {
const dbQuery = this.knex(collection).count('*', { as: 'count' }).first();
if (this.accountability?.admin !== true) {
const permissionsRecord = this.accountability?.permissions?.find((permission) => {
return permission.action === 'read' && permission.collection === collection;
});
if (!permissionsRecord) throw new ForbiddenError();
const permissions = permissionsRecord.permissions ?? {};
applyFilter(this.knex, this.schema, dbQuery, permissions, collection, {});
}
const result = await dbQuery;
return Number(result?.count ?? 0);
}
async filterCount(collection: string, query: Query): Promise<number> {
const dbQuery = this.knex(collection);
let filter = query.filter || {};
let hasJoins = false;
if (this.accountability?.admin !== true) {
const permissionsRecord = this.accountability?.permissions?.find((permission) => {
return permission.action === 'read' && permission.collection === collection;
});
if (!permissionsRecord) throw new ForbiddenError();
const permissions = permissionsRecord.permissions ?? {};
if (Object.keys(filter).length > 0) {
filter = { _and: [permissions, filter] };
} else {
filter = permissions;
}
}
if (Object.keys(filter).length > 0) {
({ hasJoins } = applyFilter(this.knex, this.schema, dbQuery, filter, collection, {}));
}
if (query.search) {
applySearch(this.schema, dbQuery, query.search, collection);
}
if (hasJoins) {
const primaryKeyName = this.schema.collections[collection]!.primary;
dbQuery.countDistinct({ count: [`${collection}.${primaryKeyName}`] });
} else {
dbQuery.count('*', { as: 'count' });
}
const records = await dbQuery;
return Number(records[0]!['count']);
}
}