directus 9.2.0 → 9.4.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.
Files changed (90) hide show
  1. package/dist/app.js +5 -3
  2. package/dist/auth/auth.d.ts +4 -6
  3. package/dist/auth/auth.js +5 -9
  4. package/dist/auth/drivers/ldap.d.ts +3 -3
  5. package/dist/auth/drivers/ldap.js +11 -4
  6. package/dist/auth/drivers/local.d.ts +2 -2
  7. package/dist/auth/drivers/local.js +5 -12
  8. package/dist/auth/drivers/oauth2.d.ts +4 -4
  9. package/dist/auth/drivers/oauth2.js +47 -21
  10. package/dist/auth/drivers/openid.d.ts +4 -4
  11. package/dist/auth/drivers/openid.js +35 -19
  12. package/dist/cli/commands/bootstrap/index.js +3 -2
  13. package/dist/cli/commands/init/index.js +3 -7
  14. package/dist/cli/commands/schema/apply.js +1 -1
  15. package/dist/cli/utils/defaults.d.ts +11 -0
  16. package/dist/cli/utils/defaults.js +14 -0
  17. package/dist/constants.d.ts +8 -0
  18. package/dist/constants.js +16 -2
  19. package/dist/controllers/shares.d.ts +2 -0
  20. package/dist/controllers/shares.js +212 -0
  21. package/dist/controllers/users.js +21 -9
  22. package/dist/database/migrations/20211211A-add-shares.d.ts +3 -0
  23. package/dist/database/migrations/20211211A-add-shares.js +37 -0
  24. package/dist/database/run-ast.js +5 -5
  25. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -15
  26. package/dist/database/system-data/app-access-permissions/index.d.ts +1 -0
  27. package/dist/database/system-data/app-access-permissions/index.js +4 -2
  28. package/dist/database/system-data/app-access-permissions/schema-access-permissions.yaml +17 -0
  29. package/dist/database/system-data/collections/collections.yaml +3 -0
  30. package/dist/database/system-data/fields/sessions.yaml +1 -1
  31. package/dist/database/system-data/fields/shares.yaml +73 -0
  32. package/dist/database/system-data/fields/users.yaml +1 -1
  33. package/dist/database/system-data/relations/relations.yaml +15 -0
  34. package/dist/emitter.d.ts +3 -2
  35. package/dist/emitter.js +13 -6
  36. package/dist/exceptions/index.d.ts +2 -0
  37. package/dist/exceptions/index.js +2 -0
  38. package/dist/exceptions/invalid-token.d.ts +4 -0
  39. package/dist/exceptions/invalid-token.js +10 -0
  40. package/dist/exceptions/unexpected-response.d.ts +4 -0
  41. package/dist/exceptions/unexpected-response.js +10 -0
  42. package/dist/extensions.d.ts +1 -0
  43. package/dist/extensions.js +10 -4
  44. package/dist/middleware/authenticate.js +5 -15
  45. package/dist/middleware/check-ip.js +9 -6
  46. package/dist/middleware/respond.js +4 -1
  47. package/dist/services/activity.d.ts +2 -1
  48. package/dist/services/activity.js +2 -2
  49. package/dist/services/authentication.d.ts +2 -7
  50. package/dist/services/authentication.js +81 -41
  51. package/dist/services/authorization.js +3 -3
  52. package/dist/services/collections.d.ts +1 -2
  53. package/dist/services/collections.js +2 -2
  54. package/dist/services/files.d.ts +2 -2
  55. package/dist/services/files.js +14 -8
  56. package/dist/services/graphql.js +16 -5
  57. package/dist/services/index.d.ts +1 -0
  58. package/dist/services/index.js +1 -0
  59. package/dist/services/items.d.ts +1 -15
  60. package/dist/services/notifications.d.ts +2 -2
  61. package/dist/services/permissions.d.ts +2 -2
  62. package/dist/services/roles.d.ts +2 -2
  63. package/dist/services/shares.d.ts +17 -0
  64. package/dist/services/shares.js +135 -0
  65. package/dist/services/specifications.js +1 -1
  66. package/dist/services/users.d.ts +2 -2
  67. package/dist/services/users.js +8 -6
  68. package/dist/services/webhooks.d.ts +2 -2
  69. package/dist/tests/database/migrations/run.test.d.ts +1 -0
  70. package/dist/tests/database/migrations/run.test.js +29 -0
  71. package/dist/types/ast.d.ts +3 -3
  72. package/dist/types/auth.d.ts +31 -0
  73. package/dist/types/extensions.d.ts +2 -0
  74. package/dist/types/items.d.ts +14 -0
  75. package/dist/utils/apply-query.d.ts +0 -38
  76. package/dist/utils/apply-query.js +67 -69
  77. package/dist/utils/apply-snapshot.js +69 -14
  78. package/dist/utils/get-ast-from-query.js +3 -3
  79. package/dist/utils/get-permissions.d.ts +2 -2
  80. package/dist/utils/get-permissions.js +117 -72
  81. package/dist/utils/get-relation-type.d.ts +1 -1
  82. package/dist/utils/get-relation-type.js +1 -1
  83. package/dist/utils/merge-permissions-for-share.d.ts +5 -0
  84. package/dist/utils/merge-permissions-for-share.js +116 -0
  85. package/dist/utils/merge-permissions.d.ts +13 -1
  86. package/dist/utils/merge-permissions.js +29 -21
  87. package/dist/utils/reduce-schema.d.ts +2 -2
  88. package/dist/utils/reduce-schema.js +7 -7
  89. package/dist/utils/user-name.js +3 -0
  90. package/package.json +14 -13
@@ -34,6 +34,7 @@ const revisions_1 = require("./revisions");
34
34
  const roles_1 = require("./roles");
35
35
  const server_1 = require("./server");
36
36
  const settings_1 = require("./settings");
37
+ const shares_1 = require("./shares");
37
38
  const specifications_1 = require("./specifications");
38
39
  const tfa_1 = require("./tfa");
39
40
  const users_1 = require("./users");
@@ -112,15 +113,23 @@ class GraphQLService {
112
113
  return formattedResult;
113
114
  }
114
115
  getSchema(type = 'schema') {
115
- var _a, _b, _c, _d;
116
+ var _a, _b, _c, _d, _e, _f, _g, _h;
116
117
  // eslint-disable-next-line @typescript-eslint/no-this-alias
117
118
  const self = this;
118
119
  const schemaComposer = new graphql_compose_1.SchemaComposer();
119
120
  const schema = {
120
- read: ((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) === true ? this.schema : (0, reduce_schema_1.reduceSchema)(this.schema, this.accountability, ['read']),
121
- create: ((_b = this.accountability) === null || _b === void 0 ? void 0 : _b.admin) === true ? this.schema : (0, reduce_schema_1.reduceSchema)(this.schema, this.accountability, ['create']),
122
- update: ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) === true ? this.schema : (0, reduce_schema_1.reduceSchema)(this.schema, this.accountability, ['update']),
123
- delete: ((_d = this.accountability) === null || _d === void 0 ? void 0 : _d.admin) === true ? this.schema : (0, reduce_schema_1.reduceSchema)(this.schema, this.accountability, ['delete']),
121
+ read: ((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) === true
122
+ ? this.schema
123
+ : (0, reduce_schema_1.reduceSchema)(this.schema, ((_b = this.accountability) === null || _b === void 0 ? void 0 : _b.permissions) || null, ['read']),
124
+ create: ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) === true
125
+ ? this.schema
126
+ : (0, reduce_schema_1.reduceSchema)(this.schema, ((_d = this.accountability) === null || _d === void 0 ? void 0 : _d.permissions) || null, ['create']),
127
+ update: ((_e = this.accountability) === null || _e === void 0 ? void 0 : _e.admin) === true
128
+ ? this.schema
129
+ : (0, reduce_schema_1.reduceSchema)(this.schema, ((_f = this.accountability) === null || _f === void 0 ? void 0 : _f.permissions) || null, ['update']),
130
+ delete: ((_g = this.accountability) === null || _g === void 0 ? void 0 : _g.admin) === true
131
+ ? this.schema
132
+ : (0, reduce_schema_1.reduceSchema)(this.schema, ((_h = this.accountability) === null || _h === void 0 ? void 0 : _h.permissions) || null, ['delete']),
124
133
  };
125
134
  const { ReadCollectionTypes } = getReadableTypes();
126
135
  const { CreateCollectionTypes, UpdateCollectionTypes, DeleteCollectionTypes } = getWritableTypes();
@@ -1268,6 +1277,8 @@ class GraphQLService {
1268
1277
  return new users_1.UsersService(opts);
1269
1278
  case 'directus_webhooks':
1270
1279
  return new webhooks_1.WebhooksService(opts);
1280
+ case 'directus_shares':
1281
+ return new shares_1.SharesService(opts);
1271
1282
  default:
1272
1283
  return new items_1.ItemsService(collection, opts);
1273
1284
  }
@@ -26,3 +26,4 @@ export * from './tfa';
26
26
  export * from './users';
27
27
  export * from './utils';
28
28
  export * from './webhooks';
29
+ export * from './shares';
@@ -39,3 +39,4 @@ __exportStar(require("./tfa"), exports);
39
39
  __exportStar(require("./users"), exports);
40
40
  __exportStar(require("./utils"), exports);
41
41
  __exportStar(require("./webhooks"), exports);
42
+ __exportStar(require("./shares"), exports);
@@ -1,25 +1,11 @@
1
1
  import { Knex } from 'knex';
2
2
  import Keyv from 'keyv';
3
3
  import { Accountability, Query, PermissionsAction } from '@directus/shared/types';
4
- import { AbstractService, AbstractServiceOptions, Item as AnyItem, PrimaryKey, SchemaOverview } from '../types';
4
+ import { AbstractService, AbstractServiceOptions, Item as AnyItem, PrimaryKey, SchemaOverview, MutationOptions } from '../types';
5
5
  export declare type QueryOptions = {
6
6
  stripNonRequested?: boolean;
7
7
  permissionsAction?: PermissionsAction;
8
8
  };
9
- export declare type MutationOptions = {
10
- /**
11
- * Callback function that's fired whenever a revision is made in the mutation
12
- */
13
- onRevisionCreate?: (pk: PrimaryKey) => void;
14
- /**
15
- * Flag to disable the auto purging of the cache. Is ignored when CACHE_AUTO_PURGE isn't enabled.
16
- */
17
- autoPurgeCache?: false;
18
- /**
19
- * Allow disabling the emitting of hooks. Useful if a custom hook is fired (like files.upload)
20
- */
21
- emitEvents?: boolean;
22
- };
23
9
  export declare class ItemsService<Item extends AnyItem = AnyItem> implements AbstractService {
24
10
  collection: string;
25
11
  knex: Knex;
@@ -1,6 +1,6 @@
1
1
  import { UsersService, MailService } from '.';
2
- import { AbstractServiceOptions, PrimaryKey } from '../types';
3
- import { ItemsService, MutationOptions } from './items';
2
+ import { AbstractServiceOptions, PrimaryKey, MutationOptions } from '../types';
3
+ import { ItemsService } from './items';
4
4
  import { Notification } from '@directus/shared/types';
5
5
  export declare class NotificationsService extends ItemsService {
6
6
  usersService: UsersService;
@@ -1,5 +1,5 @@
1
- import { ItemsService, QueryOptions, MutationOptions } from '../services/items';
2
- import { AbstractServiceOptions, Item, PrimaryKey } from '../types';
1
+ import { ItemsService, QueryOptions } from '../services/items';
2
+ import { AbstractServiceOptions, Item, PrimaryKey, MutationOptions } from '../types';
3
3
  import { Query, PermissionsAction } from '@directus/shared/types';
4
4
  import Keyv from 'keyv';
5
5
  export declare class PermissionsService extends ItemsService {
@@ -1,6 +1,6 @@
1
- import { AbstractServiceOptions, PrimaryKey } from '../types';
1
+ import { AbstractServiceOptions, MutationOptions, PrimaryKey } from '../types';
2
2
  import { Query } from '@directus/shared/types';
3
- import { ItemsService, MutationOptions } from './items';
3
+ import { ItemsService } from './items';
4
4
  export declare class RolesService extends ItemsService {
5
5
  constructor(options: AbstractServiceOptions);
6
6
  private checkForOtherAdminRoles;
@@ -0,0 +1,17 @@
1
+ import { AbstractServiceOptions, LoginResult, Item, PrimaryKey, MutationOptions } from '../types';
2
+ import { ItemsService } from './items';
3
+ import { AuthorizationService } from './authorization';
4
+ export declare class SharesService extends ItemsService {
5
+ authorizationService: AuthorizationService;
6
+ constructor(options: AbstractServiceOptions);
7
+ createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
8
+ login(payload: Record<string, any>): Promise<LoginResult>;
9
+ /**
10
+ * Send a link to the given share ID to the given email(s). Note: you can only send a link to a share
11
+ * if you have read access to that particular share
12
+ */
13
+ invite(payload: {
14
+ emails: string[];
15
+ share: PrimaryKey;
16
+ }): Promise<void>;
17
+ }
@@ -0,0 +1,135 @@
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
+ exports.SharesService = void 0;
7
+ const items_1 = require("./items");
8
+ const argon2_1 = __importDefault(require("argon2"));
9
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
10
+ const ms_1 = __importDefault(require("ms"));
11
+ const exceptions_1 = require("../exceptions");
12
+ const env_1 = __importDefault(require("../env"));
13
+ const nanoid_1 = require("nanoid");
14
+ const authorization_1 = require("./authorization");
15
+ const users_1 = require("./users");
16
+ const mail_1 = require("./mail");
17
+ const user_name_1 = require("../utils/user-name");
18
+ const md_1 = require("../utils/md");
19
+ class SharesService extends items_1.ItemsService {
20
+ constructor(options) {
21
+ super('directus_shares', options);
22
+ this.authorizationService = new authorization_1.AuthorizationService({
23
+ accountability: this.accountability,
24
+ knex: this.knex,
25
+ schema: this.schema,
26
+ });
27
+ }
28
+ async createOne(data, opts) {
29
+ await this.authorizationService.checkAccess('share', data.collection, data.item);
30
+ return super.createOne(data, opts);
31
+ }
32
+ async login(payload) {
33
+ var _a, _b;
34
+ const record = await this.knex
35
+ .select({
36
+ share_id: 'id',
37
+ share_role: 'role',
38
+ share_item: 'item',
39
+ share_collection: 'collection',
40
+ share_start: 'date_start',
41
+ share_end: 'date_end',
42
+ share_times_used: 'times_used',
43
+ share_max_uses: 'max_uses',
44
+ share_password: 'password',
45
+ })
46
+ .from('directus_shares')
47
+ .where('id', payload.share)
48
+ .andWhere((subQuery) => {
49
+ subQuery.whereNull('date_end').orWhere('date_end', '>=', this.knex.fn.now());
50
+ })
51
+ .andWhere((subQuery) => {
52
+ subQuery.whereNull('date_start').orWhere('date_start', '<=', this.knex.fn.now());
53
+ })
54
+ .andWhere((subQuery) => {
55
+ subQuery.whereNull('max_uses').orWhere('max_uses', '>=', this.knex.ref('times_used'));
56
+ })
57
+ .first();
58
+ if (!record) {
59
+ throw new exceptions_1.InvalidCredentialsException();
60
+ }
61
+ if (record.share_password && !(await argon2_1.default.verify(record.share_password, payload.password))) {
62
+ throw new exceptions_1.InvalidCredentialsException();
63
+ }
64
+ await this.knex('directus_shares')
65
+ .update({ times_used: record.share_times_used + 1 })
66
+ .where('id', record.share_id);
67
+ const tokenPayload = {
68
+ app_access: false,
69
+ admin_access: false,
70
+ role: record.share_role,
71
+ share: record.share_id,
72
+ share_scope: {
73
+ item: record.share_item,
74
+ collection: record.share_collection,
75
+ },
76
+ };
77
+ const accessToken = jsonwebtoken_1.default.sign(tokenPayload, env_1.default.SECRET, {
78
+ expiresIn: env_1.default.ACCESS_TOKEN_TTL,
79
+ issuer: 'directus',
80
+ });
81
+ const refreshToken = (0, nanoid_1.nanoid)(64);
82
+ const refreshTokenExpiration = new Date(Date.now() + (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL));
83
+ await this.knex('directus_sessions').insert({
84
+ token: refreshToken,
85
+ expires: refreshTokenExpiration,
86
+ ip: (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.ip,
87
+ user_agent: (_b = this.accountability) === null || _b === void 0 ? void 0 : _b.userAgent,
88
+ share: record.share_id,
89
+ });
90
+ await this.knex('directus_sessions').delete().where('expires', '<', new Date());
91
+ return {
92
+ accessToken,
93
+ refreshToken,
94
+ expires: (0, ms_1.default)(env_1.default.ACCESS_TOKEN_TTL),
95
+ };
96
+ }
97
+ /**
98
+ * Send a link to the given share ID to the given email(s). Note: you can only send a link to a share
99
+ * if you have read access to that particular share
100
+ */
101
+ async invite(payload) {
102
+ var _a;
103
+ if (!((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.user))
104
+ throw new exceptions_1.ForbiddenException();
105
+ const share = await this.readOne(payload.share, { fields: ['collection'] });
106
+ const usersService = new users_1.UsersService({
107
+ knex: this.knex,
108
+ schema: this.schema,
109
+ });
110
+ const mailService = new mail_1.MailService({ schema: this.schema, accountability: this.accountability });
111
+ const userInfo = await usersService.readOne(this.accountability.user, {
112
+ fields: ['first_name', 'last_name', 'email', 'id'],
113
+ });
114
+ const message = `
115
+ Hello!
116
+
117
+ ${(0, user_name_1.userName)(userInfo)} has invited you to view an item in ${share.collection}.
118
+
119
+ [Open](${env_1.default.PUBLIC_URL}/admin/shared/${payload.share})
120
+ `;
121
+ for (const email of payload.emails) {
122
+ await mailService.send({
123
+ template: {
124
+ name: 'base',
125
+ data: {
126
+ html: (0, md_1.md)(message),
127
+ },
128
+ },
129
+ to: email,
130
+ subject: `${(0, user_name_1.userName)(userInfo)} has shared an item with you`,
131
+ });
132
+ }
133
+ }
134
+ }
135
+ exports.SharesService = SharesService;
@@ -442,7 +442,7 @@ class OASSpecsService {
442
442
  ],
443
443
  };
444
444
  }
445
- else if (relationType === 'm2a') {
445
+ else if (relationType === 'a2o') {
446
446
  const relatedTags = tags.filter((tag) => relation.meta.one_allowed_collections.includes(tag['x-collection']));
447
447
  propertyObject.type = 'array';
448
448
  propertyObject.items = {
@@ -1,8 +1,8 @@
1
1
  import { Knex } from 'knex';
2
- import { AbstractServiceOptions, Item, PrimaryKey, SchemaOverview } from '../types';
2
+ import { AbstractServiceOptions, Item, PrimaryKey, SchemaOverview, MutationOptions } from '../types';
3
3
  import { Query } from '@directus/shared/types';
4
4
  import { Accountability } from '@directus/shared/types';
5
- import { ItemsService, MutationOptions } from './items';
5
+ import { ItemsService } from './items';
6
6
  export declare class UsersService extends ItemsService {
7
7
  knex: Knex;
8
8
  accountability: Accountability | null;
@@ -19,6 +19,7 @@ const mail_1 = require("./mail");
19
19
  const settings_1 = require("./settings");
20
20
  const stall_1 = require("../utils/stall");
21
21
  const perf_hooks_1 = require("perf_hooks");
22
+ const utils_2 = require("@directus/shared/utils");
22
23
  class UsersService extends items_1.ItemsService {
23
24
  constructor(options) {
24
25
  super('directus_users', options);
@@ -251,7 +252,7 @@ class UsersService extends items_1.ItemsService {
251
252
  }
252
253
  const STALL_TIME = 500;
253
254
  const timeStart = perf_hooks_1.performance.now();
254
- const user = await this.knex.select('status').from('directus_users').where({ email }).first();
255
+ const user = await this.knex.select('status', 'password').from('directus_users').where({ email }).first();
255
256
  if ((user === null || user === void 0 ? void 0 : user.status) !== 'active') {
256
257
  await (0, stall_1.stall)(STALL_TIME, timeStart);
257
258
  throw new exceptions_2.ForbiddenException();
@@ -261,7 +262,7 @@ class UsersService extends items_1.ItemsService {
261
262
  knex: this.knex,
262
263
  accountability: this.accountability,
263
264
  });
264
- const payload = { email, scope: 'password-reset' };
265
+ const payload = { email, scope: 'password-reset', hash: (0, utils_2.getSimpleHash)('' + user.password) };
265
266
  const token = jsonwebtoken_1.default.sign(payload, env_1.default.SECRET, { expiresIn: '1d', issuer: 'directus' });
266
267
  const acceptURL = url ? `${url}?token=${token}` : `${env_1.default.PUBLIC_URL}/admin/reset-password?token=${token}`;
267
268
  const subjectLine = subject ? subject : 'Password Reset Request';
@@ -279,11 +280,12 @@ class UsersService extends items_1.ItemsService {
279
280
  await (0, stall_1.stall)(STALL_TIME, timeStart);
280
281
  }
281
282
  async resetPassword(token, password) {
282
- const { email, scope } = jsonwebtoken_1.default.verify(token, env_1.default.SECRET, { issuer: 'directus' });
283
- if (scope !== 'password-reset')
283
+ const { email, scope, hash } = jsonwebtoken_1.default.verify(token, env_1.default.SECRET, { issuer: 'directus' });
284
+ if (scope !== 'password-reset' || !hash)
284
285
  throw new exceptions_2.ForbiddenException();
285
- const user = await this.knex.select('id', 'status').from('directus_users').where({ email }).first();
286
- if ((user === null || user === void 0 ? void 0 : user.status) !== 'active') {
286
+ await this.checkPasswordPolicy([password]);
287
+ const user = await this.knex.select('id', 'status', 'password').from('directus_users').where({ email }).first();
288
+ if ((user === null || user === void 0 ? void 0 : user.status) !== 'active' || hash !== (0, utils_2.getSimpleHash)('' + user.password)) {
287
289
  throw new exceptions_2.ForbiddenException();
288
290
  }
289
291
  // Allow unauthenticated update
@@ -1,5 +1,5 @@
1
- import { AbstractServiceOptions, Item, PrimaryKey, Webhook } from '../types';
2
- import { ItemsService, MutationOptions } from './items';
1
+ import { AbstractServiceOptions, Item, PrimaryKey, Webhook, MutationOptions } from '../types';
2
+ import { ItemsService } from './items';
3
3
  export declare class WebhooksService extends ItemsService<Webhook> {
4
4
  constructor(options: AbstractServiceOptions);
5
5
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
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 knex_1 = __importDefault(require("knex"));
7
+ const knex_mock_client_1 = require("knex-mock-client");
8
+ const run_1 = __importDefault(require("../../../database/migrations/run"));
9
+ describe('run', () => {
10
+ let db;
11
+ let tracker;
12
+ beforeAll(() => {
13
+ db = (0, knex_1.default)({ client: knex_mock_client_1.MockClient });
14
+ tracker = (0, knex_mock_client_1.getTracker)();
15
+ });
16
+ afterEach(() => {
17
+ tracker.reset();
18
+ });
19
+ describe('when passed the argument up', () => {
20
+ it('returns "Nothing To Updage" if no directus_migrations', async () => {
21
+ // note the difference between an empty array and ['Empty']
22
+ tracker.on.select('directus_migrations').response(['Empty']);
23
+ await (0, run_1.default)(db, 'up').catch((e) => {
24
+ expect(e).toBeInstanceOf(Error);
25
+ expect(e.message).toBe('Nothing to upgrade');
26
+ });
27
+ });
28
+ });
29
+ });
@@ -10,8 +10,8 @@ export declare type M2ONode = {
10
10
  parentKey: string;
11
11
  relatedKey: string;
12
12
  };
13
- export declare type M2ANode = {
14
- type: 'm2a';
13
+ export declare type A2MNode = {
14
+ type: 'a2o';
15
15
  names: string[];
16
16
  children: {
17
17
  [collection: string]: (NestedCollectionNode | FieldNode)[];
@@ -36,7 +36,7 @@ export declare type O2MNode = {
36
36
  parentKey: string;
37
37
  relatedKey: string;
38
38
  };
39
- export declare type NestedCollectionNode = M2ONode | O2MNode | M2ANode;
39
+ export declare type NestedCollectionNode = M2ONode | O2MNode | A2MNode;
40
40
  export declare type FieldNode = {
41
41
  type: 'field';
42
42
  name: string;
@@ -15,11 +15,42 @@ export interface User {
15
15
  provider: string;
16
16
  external_identifier: string | null;
17
17
  auth_data: string | Record<string, unknown> | null;
18
+ app_access: boolean;
19
+ admin_access: boolean;
18
20
  }
19
21
  export declare type AuthData = Record<string, any> | null;
20
22
  export interface Session {
21
23
  token: string;
22
24
  expires: Date;
23
25
  data: string | Record<string, unknown> | null;
26
+ share: string;
24
27
  }
25
28
  export declare type SessionData = Record<string, any> | null;
29
+ export declare type DirectusTokenPayload = {
30
+ id?: string;
31
+ role: string | null;
32
+ app_access: boolean | number;
33
+ admin_access: boolean | number;
34
+ share?: string;
35
+ share_scope?: {
36
+ collection: string;
37
+ item: string;
38
+ };
39
+ };
40
+ export declare type ShareData = {
41
+ share_id: string;
42
+ share_role: string;
43
+ share_item: string;
44
+ share_collection: string;
45
+ share_start: Date;
46
+ share_end: Date;
47
+ share_times_used: number;
48
+ share_max_uses?: number;
49
+ share_password?: string;
50
+ };
51
+ export declare type LoginResult = {
52
+ accessToken: any;
53
+ refreshToken: any;
54
+ expires: any;
55
+ id?: any;
56
+ };
@@ -5,6 +5,7 @@ import { Logger } from 'pino';
5
5
  import env from '../env';
6
6
  import * as exceptions from '../exceptions';
7
7
  import * as services from '../services';
8
+ import { Emitter } from '../emitter';
8
9
  import { getSchema } from '../utils/get-schema';
9
10
  import { SchemaOverview } from './schema';
10
11
  export declare type ExtensionContext = {
@@ -12,6 +13,7 @@ export declare type ExtensionContext = {
12
13
  exceptions: typeof exceptions;
13
14
  database: Knex;
14
15
  env: typeof env;
16
+ emitter: Emitter;
15
17
  logger: Logger;
16
18
  getSchema: typeof getSchema;
17
19
  };
@@ -13,3 +13,17 @@ export declare type Alterations = {
13
13
  }[];
14
14
  delete: (number | string)[];
15
15
  };
16
+ export declare type MutationOptions = {
17
+ /**
18
+ * Callback function that's fired whenever a revision is made in the mutation
19
+ */
20
+ onRevisionCreate?: (pk: PrimaryKey) => void;
21
+ /**
22
+ * Flag to disable the auto purging of the cache. Is ignored when CACHE_AUTO_PURGE isn't enabled.
23
+ */
24
+ autoPurgeCache?: false;
25
+ /**
26
+ * Allow disabling the emitting of hooks. Useful if a custom hook is fired (like files.upload)
27
+ */
28
+ emitEvents?: boolean;
29
+ };
@@ -5,44 +5,6 @@ import { Aggregate, Filter, Query } from '@directus/shared/types';
5
5
  * Apply the Query to a given Knex query builder instance
6
6
  */
7
7
  export default function applyQuery(knex: Knex, collection: string, dbQuery: Knex.QueryBuilder, query: Query, schema: SchemaOverview, subQuery?: boolean): Knex.QueryBuilder;
8
- /**
9
- * Apply a given filter object to the Knex QueryBuilder instance.
10
- *
11
- * Relational nested filters, like the following example:
12
- *
13
- * ```json
14
- * // Fetch pages that have articles written by Rijk
15
- *
16
- * {
17
- * "articles": {
18
- * "author": {
19
- * "name": {
20
- * "_eq": "Rijk"
21
- * }
22
- * }
23
- * }
24
- * }
25
- * ```
26
- *
27
- * are handled by joining the nested tables, and using a where statement on the top level on the
28
- * nested field through the join. This allows us to filter the top level items based on nested data.
29
- * The where on the root is done with a subquery to prevent duplicates, any nested joins are done
30
- * with aliases to prevent naming conflicts.
31
- *
32
- * The output SQL for the above would look something like:
33
- *
34
- * ```sql
35
- * SELECT *
36
- * FROM pages
37
- * WHERE
38
- * pages.id in (
39
- * SELECT articles.page_id AS page_id
40
- * FROM articles
41
- * LEFT JOIN authors AS xviqp ON articles.author = xviqp.id
42
- * WHERE xviqp.name = 'Rijk'
43
- * )
44
- * ```
45
- */
46
8
  export declare function applyFilter(knex: Knex, schema: SchemaOverview, rootQuery: Knex.QueryBuilder, rootFilter: Filter, collection: string, subQuery?: boolean): Knex.QueryBuilder<any, any>;
47
9
  export declare function applySearch(schema: SchemaOverview, dbQuery: Knex.QueryBuilder, searchQuery: string, collection: string): Promise<void>;
48
10
  export declare function applyAggregate(dbQuery: Knex.QueryBuilder, aggregate: Aggregate, collection: string): void;