directus 9.15.1 → 9.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__utils__/items-utils.d.ts +2 -0
- package/dist/__utils__/items-utils.js +36 -0
- package/dist/__utils__/schemas.d.ts +13 -0
- package/dist/__utils__/schemas.js +304 -0
- package/dist/__utils__/snapshots.d.ts +5 -0
- package/dist/__utils__/snapshots.js +897 -0
- package/dist/app.js +8 -7
- package/dist/auth/drivers/ldap.js +1 -0
- package/dist/auth/drivers/local.js +6 -0
- package/dist/auth/drivers/oauth2.js +1 -0
- package/dist/auth/drivers/openid.js +1 -0
- package/dist/cli/index.test.d.ts +1 -0
- package/dist/cli/index.test.js +58 -0
- package/dist/cli/utils/create-env/env-stub.liquid +6 -2
- package/dist/controllers/activity.js +1 -0
- package/dist/controllers/assets.js +20 -16
- package/dist/controllers/auth.js +6 -9
- package/dist/controllers/files.test.d.ts +1 -0
- package/dist/controllers/files.test.js +49 -0
- package/dist/controllers/server.js +0 -1
- package/dist/database/migrations/20220826A-add-origin-to-accountability.d.ts +3 -0
- package/dist/database/migrations/20220826A-add-origin-to-accountability.js +21 -0
- package/dist/database/migrations/run.test.d.ts +1 -0
- package/dist/database/migrations/run.test.js +92 -0
- package/dist/database/system-data/fields/activity.yaml +6 -0
- package/dist/database/system-data/fields/sessions.yaml +2 -0
- package/dist/env.js +15 -0
- package/dist/env.test.d.ts +8 -0
- package/dist/env.test.js +39 -0
- package/dist/extensions.d.ts +1 -0
- package/dist/extensions.js +16 -3
- package/dist/flows.js +28 -17
- package/dist/mailer.js +1 -0
- package/dist/middleware/authenticate.d.ts +1 -1
- package/dist/middleware/authenticate.js +1 -0
- package/dist/middleware/authenticate.test.d.ts +1 -0
- package/dist/middleware/authenticate.test.js +214 -0
- package/dist/middleware/extract-token.test.d.ts +1 -0
- package/dist/middleware/extract-token.test.js +60 -0
- package/dist/middleware/validate-batch.d.ts +1 -2
- package/dist/middleware/validate-batch.js +10 -13
- package/dist/middleware/validate-batch.test.d.ts +1 -0
- package/dist/middleware/validate-batch.test.js +82 -0
- package/dist/operations/exec/index.d.ts +5 -0
- package/dist/operations/exec/index.js +26 -0
- package/dist/operations/exec/index.test.d.ts +1 -0
- package/dist/operations/exec/index.test.js +95 -0
- package/dist/operations/notification/index.js +9 -6
- package/dist/operations/request/index.js +22 -3
- package/dist/operations/transform/index.d.ts +1 -1
- package/dist/operations/transform/index.js +1 -1
- package/dist/services/authentication.js +13 -3
- package/dist/services/files.js +3 -2
- package/dist/services/files.test.d.ts +1 -0
- package/dist/services/files.test.js +53 -0
- package/dist/services/flows.js +4 -0
- package/dist/services/graphql/index.d.ts +2 -2
- package/dist/services/graphql/index.js +78 -75
- package/dist/services/items.js +98 -42
- package/dist/services/items.test.d.ts +1 -0
- package/dist/services/items.test.js +765 -0
- package/dist/services/payload.d.ts +7 -4
- package/dist/services/payload.js +63 -12
- package/dist/services/payload.test.d.ts +1 -0
- package/dist/services/payload.test.js +94 -0
- package/dist/services/server.js +10 -7
- package/dist/services/shares.js +2 -1
- package/dist/services/specifications.test.d.ts +1 -0
- package/dist/services/specifications.test.js +96 -0
- package/dist/types/items.d.ts +11 -0
- package/dist/utils/apply-query.js +7 -3
- package/dist/utils/apply-snapshot.js +15 -0
- package/dist/utils/apply-snapshot.test.d.ts +1 -0
- package/dist/utils/apply-snapshot.test.js +305 -0
- package/dist/utils/async-handler.d.ts +2 -6
- package/dist/utils/async-handler.js +1 -13
- package/dist/utils/async-handler.test.d.ts +1 -0
- package/dist/utils/async-handler.test.js +18 -0
- package/dist/utils/calculate-field-depth.test.d.ts +1 -0
- package/dist/utils/calculate-field-depth.test.js +76 -0
- package/dist/utils/filter-items.test.d.ts +1 -0
- package/dist/utils/filter-items.test.js +60 -0
- package/dist/utils/get-cache-key.test.d.ts +1 -0
- package/dist/utils/get-cache-key.test.js +53 -0
- package/dist/utils/get-column-path.test.d.ts +1 -0
- package/dist/utils/get-column-path.test.js +136 -0
- package/dist/utils/get-config-from-env.test.d.ts +1 -0
- package/dist/utils/get-config-from-env.test.js +19 -0
- package/dist/utils/get-graphql-type.d.ts +1 -1
- package/dist/utils/get-graphql-type.js +4 -1
- package/dist/utils/get-os-info.d.ts +9 -0
- package/dist/utils/get-os-info.js +47 -0
- package/dist/utils/get-relation-info.test.d.ts +1 -0
- package/dist/utils/get-relation-info.test.js +88 -0
- package/dist/utils/get-relation-type.test.d.ts +1 -0
- package/dist/utils/get-relation-type.test.js +69 -0
- package/dist/utils/get-string-byte-size.test.d.ts +1 -0
- package/dist/utils/get-string-byte-size.test.js +8 -0
- package/dist/utils/is-directus-jwt.test.d.ts +1 -0
- package/dist/utils/is-directus-jwt.test.js +26 -0
- package/dist/utils/jwt.test.d.ts +1 -0
- package/dist/utils/jwt.test.js +36 -0
- package/dist/utils/merge-permissions.test.d.ts +1 -0
- package/dist/utils/merge-permissions.test.js +80 -0
- package/dist/utils/validate-keys.test.d.ts +1 -0
- package/dist/utils/validate-keys.test.js +97 -0
- package/package.json +14 -12
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vm2_1 = require("vm2");
|
|
7
|
+
const index_1 = __importDefault(require("./index"));
|
|
8
|
+
test('Rejects when modules are used without modules being allowed', async () => {
|
|
9
|
+
const testCode = `
|
|
10
|
+
const test = require('test');
|
|
11
|
+
`;
|
|
12
|
+
await expect(index_1.default.handler({ code: testCode }, {
|
|
13
|
+
data: {},
|
|
14
|
+
env: {
|
|
15
|
+
FLOWS_EXEC_ALLOWED_MODULES: '',
|
|
16
|
+
},
|
|
17
|
+
})).rejects.toEqual(new vm2_1.VMError("Cannot find module 'test'"));
|
|
18
|
+
});
|
|
19
|
+
test('Rejects when code contains syntax errors', async () => {
|
|
20
|
+
const testCode = `
|
|
21
|
+
~~
|
|
22
|
+
`;
|
|
23
|
+
await expect(index_1.default.handler({ code: testCode }, {
|
|
24
|
+
data: {},
|
|
25
|
+
env: {
|
|
26
|
+
FLOWS_EXEC_ALLOWED_MODULES: '',
|
|
27
|
+
},
|
|
28
|
+
})).rejects.toEqual(new Error("Couldn't compile code: Unexpected end of input"));
|
|
29
|
+
});
|
|
30
|
+
test('Rejects when returned function does something illegal', async () => {
|
|
31
|
+
const testCode = `
|
|
32
|
+
module.exports = function() {
|
|
33
|
+
return a + b;
|
|
34
|
+
};
|
|
35
|
+
`;
|
|
36
|
+
await expect(index_1.default.handler({ code: testCode }, {
|
|
37
|
+
data: {},
|
|
38
|
+
env: {
|
|
39
|
+
FLOWS_EXEC_ALLOWED_MODULES: '',
|
|
40
|
+
},
|
|
41
|
+
})).rejects.toEqual(new ReferenceError('a is not defined'));
|
|
42
|
+
});
|
|
43
|
+
test("Rejects when code doesn't return valid function", async () => {
|
|
44
|
+
const testCode = `
|
|
45
|
+
module.exports = false;
|
|
46
|
+
`;
|
|
47
|
+
await expect(index_1.default.handler({ code: testCode }, {
|
|
48
|
+
data: {},
|
|
49
|
+
env: {
|
|
50
|
+
FLOWS_EXEC_ALLOWED_MODULES: '',
|
|
51
|
+
},
|
|
52
|
+
})).rejects.toEqual(new TypeError('fn is not a function'));
|
|
53
|
+
});
|
|
54
|
+
test('Rejects returned function throws errors', async () => {
|
|
55
|
+
const testCode = `
|
|
56
|
+
module.exports = function () {
|
|
57
|
+
throw new Error('test');
|
|
58
|
+
};
|
|
59
|
+
`;
|
|
60
|
+
await expect(index_1.default.handler({ code: testCode }, {
|
|
61
|
+
data: {},
|
|
62
|
+
env: {
|
|
63
|
+
FLOWS_EXEC_ALLOWED_MODULES: '',
|
|
64
|
+
},
|
|
65
|
+
})).rejects.toEqual(new Error('test'));
|
|
66
|
+
});
|
|
67
|
+
test('Executes function when valid', () => {
|
|
68
|
+
const testCode = `
|
|
69
|
+
module.exports = function (data) {
|
|
70
|
+
return { result: data.input + ' test' };
|
|
71
|
+
};
|
|
72
|
+
`;
|
|
73
|
+
expect(index_1.default.handler({ code: testCode }, {
|
|
74
|
+
data: {
|
|
75
|
+
input: 'start',
|
|
76
|
+
},
|
|
77
|
+
env: {
|
|
78
|
+
FLOWS_EXEC_ALLOWED_MODULES: '',
|
|
79
|
+
},
|
|
80
|
+
})).resolves.toEqual({ result: 'start test' });
|
|
81
|
+
});
|
|
82
|
+
test('Allows modules that are whitelisted', () => {
|
|
83
|
+
const testCode = `
|
|
84
|
+
const bytes = require('bytes');
|
|
85
|
+
|
|
86
|
+
module.exports = function (data) {
|
|
87
|
+
return { result: bytes(1000) };
|
|
88
|
+
};
|
|
89
|
+
`;
|
|
90
|
+
expect(index_1.default.handler({ code: testCode }, {
|
|
91
|
+
env: {
|
|
92
|
+
FLOWS_EXEC_ALLOWED_MODULES: 'bytes',
|
|
93
|
+
},
|
|
94
|
+
})).resolves.toEqual({ result: '1000B' });
|
|
95
|
+
});
|
|
@@ -6,7 +6,6 @@ const get_accountability_for_role_1 = require("../../utils/get-accountability-fo
|
|
|
6
6
|
exports.default = (0, utils_1.defineOperationApi)({
|
|
7
7
|
id: 'notification',
|
|
8
8
|
handler: async ({ recipient, subject, message, permissions }, { accountability, database, getSchema }) => {
|
|
9
|
-
var _a;
|
|
10
9
|
const schema = await getSchema({ database });
|
|
11
10
|
let customAccountability;
|
|
12
11
|
if (!permissions || permissions === '$trigger') {
|
|
@@ -27,12 +26,16 @@ exports.default = (0, utils_1.defineOperationApi)({
|
|
|
27
26
|
knex: database,
|
|
28
27
|
});
|
|
29
28
|
const messageString = message ? (0, utils_1.optionToString)(message) : null;
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
const payload = (0, utils_1.toArray)(recipient).map((userId) => {
|
|
30
|
+
var _a;
|
|
31
|
+
return {
|
|
32
|
+
recipient: userId,
|
|
33
|
+
sender: (_a = customAccountability === null || customAccountability === void 0 ? void 0 : customAccountability.user) !== null && _a !== void 0 ? _a : null,
|
|
34
|
+
subject,
|
|
35
|
+
message: messageString,
|
|
36
|
+
};
|
|
35
37
|
});
|
|
38
|
+
const result = await notificationsService.createMany(payload);
|
|
36
39
|
return result;
|
|
37
40
|
},
|
|
38
41
|
});
|
|
@@ -5,14 +5,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const utils_1 = require("@directus/shared/utils");
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const encodeurl_1 = __importDefault(require("encodeurl"));
|
|
8
9
|
exports.default = (0, utils_1.defineOperationApi)({
|
|
9
10
|
id: 'request',
|
|
10
11
|
handler: async ({ url, method, body, headers }) => {
|
|
11
|
-
|
|
12
|
+
var _a;
|
|
13
|
+
const customHeaders = (_a = headers === null || headers === void 0 ? void 0 : headers.reduce((acc, { header, value }) => {
|
|
12
14
|
acc[header] = value;
|
|
13
15
|
return acc;
|
|
14
|
-
}, {});
|
|
15
|
-
|
|
16
|
+
}, {})) !== null && _a !== void 0 ? _a : {};
|
|
17
|
+
if (!customHeaders['Content-Type'] && isValidJSON(body)) {
|
|
18
|
+
customHeaders['Content-Type'] = 'application/json';
|
|
19
|
+
}
|
|
20
|
+
const result = await (0, axios_1.default)({
|
|
21
|
+
url: (0, encodeurl_1.default)(url),
|
|
22
|
+
method,
|
|
23
|
+
data: body,
|
|
24
|
+
headers: customHeaders,
|
|
25
|
+
});
|
|
16
26
|
return { status: result.status, statusText: result.statusText, headers: result.headers, data: result.data };
|
|
27
|
+
function isValidJSON(value) {
|
|
28
|
+
try {
|
|
29
|
+
(0, utils_1.parseJSON)(value);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
17
36
|
},
|
|
18
37
|
});
|
|
@@ -36,15 +36,23 @@ class AuthenticationService {
|
|
|
36
36
|
* to handle password existence checks elsewhere
|
|
37
37
|
*/
|
|
38
38
|
async login(providerName = constants_1.DEFAULT_AUTH_PROVIDER, payload, otp) {
|
|
39
|
-
var _a, _b;
|
|
40
|
-
const STALL_TIME =
|
|
39
|
+
var _a, _b, _c;
|
|
40
|
+
const STALL_TIME = env_1.default.LOGIN_STALL_TIME;
|
|
41
41
|
const timeStart = perf_hooks_1.performance.now();
|
|
42
42
|
const provider = (0, auth_1.getAuthProvider)(providerName);
|
|
43
|
+
let userId;
|
|
44
|
+
try {
|
|
45
|
+
userId = await provider.getUserID((0, lodash_1.cloneDeep)(payload));
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
await (0, stall_1.stall)(STALL_TIME, timeStart);
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
43
51
|
const user = await this.knex
|
|
44
52
|
.select('u.id', 'u.first_name', 'u.last_name', 'u.email', 'u.password', 'u.status', 'u.role', 'r.admin_access', 'r.app_access', 'u.tfa_secret', 'u.provider', 'u.external_identifier', 'u.auth_data')
|
|
45
53
|
.from('directus_users as u')
|
|
46
54
|
.leftJoin('directus_roles as r', 'u.role', 'r.id')
|
|
47
|
-
.where('u.id',
|
|
55
|
+
.where('u.id', userId)
|
|
48
56
|
.first();
|
|
49
57
|
const updatedPayload = await emitter_1.default.emitFilter('auth.login', payload, {
|
|
50
58
|
status: 'pending',
|
|
@@ -151,6 +159,7 @@ class AuthenticationService {
|
|
|
151
159
|
expires: refreshTokenExpiration,
|
|
152
160
|
ip: (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.ip,
|
|
153
161
|
user_agent: (_b = this.accountability) === null || _b === void 0 ? void 0 : _b.userAgent,
|
|
162
|
+
origin: (_c = this.accountability) === null || _c === void 0 ? void 0 : _c.origin,
|
|
154
163
|
});
|
|
155
164
|
await this.knex('directus_sessions').delete().where('expires', '<', new Date());
|
|
156
165
|
if (this.accountability) {
|
|
@@ -159,6 +168,7 @@ class AuthenticationService {
|
|
|
159
168
|
user: user.id,
|
|
160
169
|
ip: this.accountability.ip,
|
|
161
170
|
user_agent: this.accountability.userAgent,
|
|
171
|
+
origin: this.accountability.origin,
|
|
162
172
|
collection: 'directus_users',
|
|
163
173
|
item: user.id,
|
|
164
174
|
});
|
package/dist/services/files.js
CHANGED
|
@@ -46,6 +46,7 @@ const utils_1 = require("@directus/shared/utils");
|
|
|
46
46
|
const items_1 = require("./items");
|
|
47
47
|
const net_1 = __importDefault(require("net"));
|
|
48
48
|
const os_1 = __importDefault(require("os"));
|
|
49
|
+
const encodeurl_1 = __importDefault(require("encodeurl"));
|
|
49
50
|
const lookupDNS = (0, util_1.promisify)(dns_1.lookup);
|
|
50
51
|
class FilesService extends items_1.ItemsService {
|
|
51
52
|
constructor(options) {
|
|
@@ -229,7 +230,7 @@ class FilesService extends items_1.ItemsService {
|
|
|
229
230
|
}
|
|
230
231
|
let fileResponse;
|
|
231
232
|
try {
|
|
232
|
-
fileResponse = await axios_1.default.get(importURL, {
|
|
233
|
+
fileResponse = await axios_1.default.get((0, encodeurl_1.default)(importURL), {
|
|
233
234
|
responseType: 'stream',
|
|
234
235
|
});
|
|
235
236
|
}
|
|
@@ -240,7 +241,7 @@ class FilesService extends items_1.ItemsService {
|
|
|
240
241
|
});
|
|
241
242
|
}
|
|
242
243
|
const parsedURL = url_1.default.parse(fileResponse.request.res.responseUrl);
|
|
243
|
-
const filename = path_1.default.basename(parsedURL.pathname);
|
|
244
|
+
const filename = decodeURI(path_1.default.basename(parsedURL.pathname));
|
|
244
245
|
const payload = {
|
|
245
246
|
filename_download: filename,
|
|
246
247
|
storage: (0, utils_1.toArray)(env_1.default.STORAGE_LOCATIONS)[0],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const exifr_1 = __importDefault(require("exifr"));
|
|
7
|
+
const knex_1 = __importDefault(require("knex"));
|
|
8
|
+
const knex_mock_client_1 = require("knex-mock-client");
|
|
9
|
+
const _1 = require(".");
|
|
10
|
+
jest.mock('exifr');
|
|
11
|
+
jest.mock('../../src/database/index', () => {
|
|
12
|
+
return { getDatabaseClient: jest.fn().mockReturnValue('postgres') };
|
|
13
|
+
});
|
|
14
|
+
jest.requireMock('../../src/database/index');
|
|
15
|
+
describe('Integration Tests', () => {
|
|
16
|
+
let db;
|
|
17
|
+
let tracker;
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
db = (0, knex_1.default)({ client: knex_mock_client_1.MockClient });
|
|
20
|
+
tracker = (0, knex_mock_client_1.getTracker)();
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
tracker.reset();
|
|
24
|
+
});
|
|
25
|
+
describe('Services / Files', () => {
|
|
26
|
+
describe('getMetadata', () => {
|
|
27
|
+
let service;
|
|
28
|
+
let exifrParseSpy;
|
|
29
|
+
const sampleMetadata = {
|
|
30
|
+
CustomTagA: 'value a',
|
|
31
|
+
CustomTagB: 'value b',
|
|
32
|
+
CustomTagC: 'value c',
|
|
33
|
+
};
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
exifrParseSpy = jest.spyOn(exifr_1.default, 'parse');
|
|
36
|
+
service = new _1.FilesService({
|
|
37
|
+
knex: db,
|
|
38
|
+
schema: { collections: {}, relations: [] },
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
it('accepts allowlist metadata tags', async () => {
|
|
42
|
+
exifrParseSpy.mockReturnValue(Promise.resolve({ ...sampleMetadata }));
|
|
43
|
+
const bufferContent = 'file buffer content';
|
|
44
|
+
const allowList = ['CustomTagB', 'CustomTagA'];
|
|
45
|
+
const metadata = await service.getMetadata(bufferContent, allowList);
|
|
46
|
+
expect(exifrParseSpy).toHaveBeenCalled();
|
|
47
|
+
expect(metadata.metadata.CustomTagA).toStrictEqual(sampleMetadata.CustomTagA);
|
|
48
|
+
expect(metadata.metadata.CustomTagB).toStrictEqual(sampleMetadata.CustomTagB);
|
|
49
|
+
expect(metadata.metadata.CustomTagC).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
package/dist/services/flows.js
CHANGED
|
@@ -39,12 +39,16 @@ class FlowsService extends items_1.ItemsService {
|
|
|
39
39
|
}
|
|
40
40
|
async deleteOne(key, opts) {
|
|
41
41
|
const flowManager = (0, flows_1.getFlowManager)();
|
|
42
|
+
// this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
|
|
43
|
+
await this.knex('directus_operations').update({ resolve: null, reject: null }).where('flow', key);
|
|
42
44
|
const result = await super.deleteOne(key, opts);
|
|
43
45
|
await flowManager.reload();
|
|
44
46
|
return result;
|
|
45
47
|
}
|
|
46
48
|
async deleteMany(keys, opts) {
|
|
47
49
|
const flowManager = (0, flows_1.getFlowManager)();
|
|
50
|
+
// this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
|
|
51
|
+
await this.knex('directus_operations').update({ resolve: null, reject: null }).whereIn('flow', keys);
|
|
48
52
|
const result = await super.deleteMany(keys, opts);
|
|
49
53
|
await flowManager.reload();
|
|
50
54
|
return result;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseException } from '@directus/shared/exceptions';
|
|
2
2
|
import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
|
|
3
|
-
import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLSchema,
|
|
3
|
+
import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLSchema, SelectionNode } from 'graphql';
|
|
4
4
|
import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
|
|
5
5
|
import { Knex } from 'knex';
|
|
6
6
|
import { AbstractServiceOptions, GraphQLParams, Item } from '../../types';
|
|
@@ -44,7 +44,7 @@ export declare class GraphQLService {
|
|
|
44
44
|
* In order to do that, we'll parse over all ArgumentNodes and ObjectFieldNodes to manually recreate an object structure
|
|
45
45
|
* of arguments
|
|
46
46
|
*/
|
|
47
|
-
parseArgs(args: readonly ArgumentNode[]
|
|
47
|
+
parseArgs(args: readonly ArgumentNode[], variableValues: GraphQLResolveInfo['variableValues']): Record<string, any>;
|
|
48
48
|
/**
|
|
49
49
|
* Get a Directus Query object from the parsed arguments (rawQuery) and GraphQL AST selectionSet. Converts SelectionSet into
|
|
50
50
|
* Directus' `fields` query for use in the resolver. Also applies variables where appropriate.
|
|
@@ -274,7 +274,7 @@ class GraphQLService {
|
|
|
274
274
|
CollectionTypes[collection.collection] = schemaComposer.createObjectTC({
|
|
275
275
|
name: action === 'read' ? collection.collection : `${action}_${collection.collection}`,
|
|
276
276
|
fields: Object.values(collection.fields).reduce((acc, field) => {
|
|
277
|
-
let type = (0, get_graphql_type_1.getGraphQLType)(field.type);
|
|
277
|
+
let type = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
|
|
278
278
|
// GraphQL doesn't differentiate between not-null and has-to-be-submitted. We
|
|
279
279
|
// can't non-null in update, as that would require every not-nullable field to be
|
|
280
280
|
// submitted on updates
|
|
@@ -293,41 +293,43 @@ class GraphQLService {
|
|
|
293
293
|
return obj[field.field];
|
|
294
294
|
},
|
|
295
295
|
};
|
|
296
|
-
if (
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
296
|
+
if (action === 'read') {
|
|
297
|
+
if (field.type === 'date') {
|
|
298
|
+
acc[`${field.field}_func`] = {
|
|
299
|
+
type: DateFunctions,
|
|
300
|
+
resolve: (obj) => {
|
|
301
|
+
const funcFields = Object.keys(DateFunctions.getFields()).map((key) => `${field.field}_${key}`);
|
|
302
|
+
return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
if (field.type === 'time') {
|
|
307
|
+
acc[`${field.field}_func`] = {
|
|
308
|
+
type: TimeFunctions,
|
|
309
|
+
resolve: (obj) => {
|
|
310
|
+
const funcFields = Object.keys(TimeFunctions.getFields()).map((key) => `${field.field}_${key}`);
|
|
311
|
+
return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
if (field.type === 'dateTime' || field.type === 'timestamp') {
|
|
316
|
+
acc[`${field.field}_func`] = {
|
|
317
|
+
type: DateTimeFunctions,
|
|
318
|
+
resolve: (obj) => {
|
|
319
|
+
const funcFields = Object.keys(DateTimeFunctions.getFields()).map((key) => `${field.field}_${key}`);
|
|
320
|
+
return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
if (field.type === 'json' || field.type === 'alias') {
|
|
325
|
+
acc[`${field.field}_func`] = {
|
|
326
|
+
type: CountFunctions,
|
|
327
|
+
resolve: (obj) => {
|
|
328
|
+
const funcFields = Object.keys(CountFunctions.getFields()).map((key) => `${field.field}_${key}`);
|
|
329
|
+
return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
331
333
|
}
|
|
332
334
|
return acc;
|
|
333
335
|
}, {}),
|
|
@@ -639,7 +641,7 @@ class GraphQLService {
|
|
|
639
641
|
ReadableCollectionFilterTypes[collection.collection] = schemaComposer.createInputTC({
|
|
640
642
|
name: `${collection.collection}_filter`,
|
|
641
643
|
fields: Object.values(collection.fields).reduce((acc, field) => {
|
|
642
|
-
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
|
|
644
|
+
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
|
|
643
645
|
let filterOperatorType;
|
|
644
646
|
switch (graphqlType) {
|
|
645
647
|
case graphql_1.GraphQLBoolean:
|
|
@@ -692,7 +694,7 @@ class GraphQLService {
|
|
|
692
694
|
AggregatedFields[collection.collection] = schemaComposer.createObjectTC({
|
|
693
695
|
name: `${collection.collection}_aggregated_fields`,
|
|
694
696
|
fields: Object.values(collection.fields).reduce((acc, field) => {
|
|
695
|
-
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
|
|
697
|
+
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
|
|
696
698
|
switch (graphqlType) {
|
|
697
699
|
case graphql_1.GraphQLInt:
|
|
698
700
|
case graphql_1.GraphQLFloat:
|
|
@@ -736,7 +738,7 @@ class GraphQLService {
|
|
|
736
738
|
},
|
|
737
739
|
};
|
|
738
740
|
const hasNumericAggregates = Object.values(collection.fields).some((field) => {
|
|
739
|
-
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
|
|
741
|
+
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
|
|
740
742
|
if (graphqlType === graphql_1.GraphQLInt || graphqlType === graphql_1.GraphQLFloat) {
|
|
741
743
|
return true;
|
|
742
744
|
}
|
|
@@ -1207,38 +1209,29 @@ class GraphQLService {
|
|
|
1207
1209
|
parseArgs(args, variableValues) {
|
|
1208
1210
|
if (!args || args.length === 0)
|
|
1209
1211
|
return {};
|
|
1210
|
-
const
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1231
|
-
else {
|
|
1232
|
-
values.push(valueNode.value);
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
argsObject[argument.name.value] = values;
|
|
1237
|
-
}
|
|
1238
|
-
else {
|
|
1239
|
-
argsObject[argument.name.value] = argument.value.value;
|
|
1212
|
+
const parse = (node) => {
|
|
1213
|
+
switch (node.kind) {
|
|
1214
|
+
case 'Variable':
|
|
1215
|
+
return variableValues[node.name.value];
|
|
1216
|
+
case 'ListValue':
|
|
1217
|
+
return node.values.map(parse);
|
|
1218
|
+
case 'ObjectValue':
|
|
1219
|
+
return Object.fromEntries(node.fields.map((node) => [node.name.value, parse(node.value)]));
|
|
1220
|
+
case 'NullValue':
|
|
1221
|
+
return null;
|
|
1222
|
+
case 'StringValue':
|
|
1223
|
+
return String(node.value);
|
|
1224
|
+
case 'IntValue':
|
|
1225
|
+
case 'FloatValue':
|
|
1226
|
+
return Number(node.value);
|
|
1227
|
+
case 'BooleanValue':
|
|
1228
|
+
return Boolean(node.value);
|
|
1229
|
+
case 'EnumValue':
|
|
1230
|
+
default:
|
|
1231
|
+
return node.value;
|
|
1240
1232
|
}
|
|
1241
|
-
}
|
|
1233
|
+
};
|
|
1234
|
+
const argsObject = Object.fromEntries(args.map((arg) => [arg.name.value, parse(arg.value)]));
|
|
1242
1235
|
return argsObject;
|
|
1243
1236
|
}
|
|
1244
1237
|
/**
|
|
@@ -1624,6 +1617,7 @@ class GraphQLService {
|
|
|
1624
1617
|
const accountability = {
|
|
1625
1618
|
ip: req === null || req === void 0 ? void 0 : req.ip,
|
|
1626
1619
|
userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
|
|
1620
|
+
origin: req === null || req === void 0 ? void 0 : req.get('origin'),
|
|
1627
1621
|
role: null,
|
|
1628
1622
|
};
|
|
1629
1623
|
const authenticationService = new authentication_1.AuthenticationService({
|
|
@@ -1658,6 +1652,7 @@ class GraphQLService {
|
|
|
1658
1652
|
const accountability = {
|
|
1659
1653
|
ip: req === null || req === void 0 ? void 0 : req.ip,
|
|
1660
1654
|
userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
|
|
1655
|
+
origin: req === null || req === void 0 ? void 0 : req.get('origin'),
|
|
1661
1656
|
role: null,
|
|
1662
1657
|
};
|
|
1663
1658
|
const authenticationService = new authentication_1.AuthenticationService({
|
|
@@ -1694,6 +1689,7 @@ class GraphQLService {
|
|
|
1694
1689
|
const accountability = {
|
|
1695
1690
|
ip: req === null || req === void 0 ? void 0 : req.ip,
|
|
1696
1691
|
userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
|
|
1692
|
+
origin: req === null || req === void 0 ? void 0 : req.get('origin'),
|
|
1697
1693
|
role: null,
|
|
1698
1694
|
};
|
|
1699
1695
|
const authenticationService = new authentication_1.AuthenticationService({
|
|
@@ -1718,6 +1714,7 @@ class GraphQLService {
|
|
|
1718
1714
|
const accountability = {
|
|
1719
1715
|
ip: req === null || req === void 0 ? void 0 : req.ip,
|
|
1720
1716
|
userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
|
|
1717
|
+
origin: req === null || req === void 0 ? void 0 : req.get('origin'),
|
|
1721
1718
|
role: null,
|
|
1722
1719
|
};
|
|
1723
1720
|
const service = new users_1.UsersService({ accountability, schema: this.schema });
|
|
@@ -1742,6 +1739,7 @@ class GraphQLService {
|
|
|
1742
1739
|
const accountability = {
|
|
1743
1740
|
ip: req === null || req === void 0 ? void 0 : req.ip,
|
|
1744
1741
|
userAgent: req === null || req === void 0 ? void 0 : req.get('user-agent'),
|
|
1742
|
+
origin: req === null || req === void 0 ? void 0 : req.get('origin'),
|
|
1745
1743
|
role: null,
|
|
1746
1744
|
};
|
|
1747
1745
|
const service = new users_1.UsersService({ accountability, schema: this.schema });
|
|
@@ -1902,7 +1900,9 @@ class GraphQLService {
|
|
|
1902
1900
|
name: 'directus_collections_meta',
|
|
1903
1901
|
fields: Object.values(schema.read.collections['directus_collections'].fields).reduce((acc, field) => {
|
|
1904
1902
|
acc[field.field] = {
|
|
1905
|
-
type: field.nullable
|
|
1903
|
+
type: field.nullable
|
|
1904
|
+
? (0, get_graphql_type_1.getGraphQLType)(field.type, field.special)
|
|
1905
|
+
: (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type, field.special)),
|
|
1906
1906
|
description: field.note,
|
|
1907
1907
|
};
|
|
1908
1908
|
return acc;
|
|
@@ -1951,7 +1951,9 @@ class GraphQLService {
|
|
|
1951
1951
|
name: 'directus_fields_meta',
|
|
1952
1952
|
fields: Object.values(schema.read.collections['directus_fields'].fields).reduce((acc, field) => {
|
|
1953
1953
|
acc[field.field] = {
|
|
1954
|
-
type: field.nullable
|
|
1954
|
+
type: field.nullable
|
|
1955
|
+
? (0, get_graphql_type_1.getGraphQLType)(field.type, field.special)
|
|
1956
|
+
: (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type, field.special)),
|
|
1955
1957
|
description: field.note,
|
|
1956
1958
|
};
|
|
1957
1959
|
return acc;
|
|
@@ -2038,7 +2040,7 @@ class GraphQLService {
|
|
|
2038
2040
|
name: 'directus_relations_meta',
|
|
2039
2041
|
fields: Object.values(schema.read.collections['directus_relations'].fields).reduce((acc, field) => {
|
|
2040
2042
|
acc[field.field] = {
|
|
2041
|
-
type: (0, get_graphql_type_1.getGraphQLType)(field.type),
|
|
2043
|
+
type: (0, get_graphql_type_1.getGraphQLType)(field.type, field.special),
|
|
2042
2044
|
description: field.note,
|
|
2043
2045
|
};
|
|
2044
2046
|
return acc;
|
|
@@ -2308,7 +2310,7 @@ class GraphQLService {
|
|
|
2308
2310
|
comment: (0, graphql_1.GraphQLNonNull)(graphql_1.GraphQLString),
|
|
2309
2311
|
},
|
|
2310
2312
|
resolve: async (_, args, __, info) => {
|
|
2311
|
-
var _a, _b, _c, _d, _e;
|
|
2313
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2312
2314
|
const service = new activity_1.ActivityService({
|
|
2313
2315
|
accountability: this.accountability,
|
|
2314
2316
|
schema: this.schema,
|
|
@@ -2319,9 +2321,10 @@ class GraphQLService {
|
|
|
2319
2321
|
user: (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.user,
|
|
2320
2322
|
ip: (_b = this.accountability) === null || _b === void 0 ? void 0 : _b.ip,
|
|
2321
2323
|
user_agent: (_c = this.accountability) === null || _c === void 0 ? void 0 : _c.userAgent,
|
|
2324
|
+
origin: (_d = this.accountability) === null || _d === void 0 ? void 0 : _d.origin,
|
|
2322
2325
|
});
|
|
2323
2326
|
if ('directus_activity' in ReadCollectionTypes) {
|
|
2324
|
-
const selections = this.replaceFragmentsInSelections((
|
|
2327
|
+
const selections = this.replaceFragmentsInSelections((_f = (_e = info.fieldNodes[0]) === null || _e === void 0 ? void 0 : _e.selectionSet) === null || _f === void 0 ? void 0 : _f.selections, info.fragments);
|
|
2325
2328
|
const query = this.getQuery(args, selections || [], info.variableValues);
|
|
2326
2329
|
return await service.readOne(primaryKey, query);
|
|
2327
2330
|
}
|