@directus/api 21.0.0 → 22.1.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/app.js +4 -4
- package/dist/auth/drivers/ldap.js +4 -4
- package/dist/auth/drivers/local.js +4 -4
- package/dist/auth/drivers/oauth2.js +4 -4
- package/dist/auth/drivers/openid.js +2 -4
- package/dist/cache.js +3 -0
- package/dist/cli/commands/bootstrap/index.js +8 -2
- package/dist/cli/commands/init/index.js +9 -10
- package/dist/cli/commands/init/questions.d.ts +7 -6
- package/dist/cli/commands/init/questions.js +2 -2
- package/dist/cli/utils/create-env/index.d.ts +2 -2
- package/dist/cli/utils/create-env/index.js +3 -1
- package/dist/cli/utils/defaults.d.ts +4 -11
- package/dist/cli/utils/defaults.js +7 -1
- package/dist/cli/utils/drivers.js +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/controllers/access.d.ts +2 -0
- package/dist/controllers/access.js +148 -0
- package/dist/controllers/auth.js +5 -16
- package/dist/controllers/permissions.js +14 -2
- package/dist/controllers/policies.d.ts +2 -0
- package/dist/controllers/policies.js +169 -0
- package/dist/controllers/roles.js +22 -1
- package/dist/controllers/tus.js +14 -26
- package/dist/controllers/users.js +0 -55
- package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +16 -0
- package/dist/database/get-ast-from-query/get-ast-from-query.js +82 -0
- package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +13 -0
- package/dist/database/get-ast-from-query/lib/convert-wildcards.js +69 -0
- package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +15 -0
- package/dist/database/get-ast-from-query/lib/parse-fields.js +200 -0
- package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +14 -0
- package/dist/database/get-ast-from-query/utils/get-deep-query.js +17 -0
- package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +2 -0
- package/dist/database/get-ast-from-query/utils/get-related-collection.js +13 -0
- package/dist/database/get-ast-from-query/utils/get-relation.d.ts +2 -0
- package/dist/database/get-ast-from-query/utils/get-relation.js +7 -0
- package/dist/database/helpers/fn/types.d.ts +2 -1
- package/dist/database/helpers/fn/types.js +1 -1
- package/dist/database/helpers/geometry/dialects/mssql.d.ts +1 -1
- package/dist/database/helpers/geometry/dialects/mssql.js +4 -2
- package/dist/database/helpers/geometry/dialects/mysql.js +1 -1
- package/dist/database/helpers/geometry/dialects/oracle.d.ts +1 -1
- package/dist/database/helpers/geometry/dialects/oracle.js +5 -3
- package/dist/database/helpers/geometry/types.d.ts +1 -1
- package/dist/database/helpers/geometry/types.js +4 -2
- package/dist/database/helpers/index.d.ts +3 -3
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/cockroachdb.js +4 -0
- package/dist/database/helpers/schema/dialects/mssql.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/mssql.js +4 -0
- package/dist/database/helpers/schema/dialects/oracle.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/oracle.js +4 -0
- package/dist/database/helpers/schema/dialects/postgres.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/postgres.js +4 -0
- package/dist/database/helpers/schema/types.d.ts +5 -0
- package/dist/database/helpers/schema/types.js +3 -0
- package/dist/database/helpers/schema/utils/preprocess-bindings.d.ts +8 -0
- package/dist/database/helpers/schema/utils/preprocess-bindings.js +30 -0
- package/dist/database/index.js +6 -1
- package/dist/database/migrations/20210519A-add-system-fk-triggers.js +3 -2
- package/dist/database/migrations/20230721A-require-shares-fields.js +3 -5
- package/dist/database/migrations/20240716A-update-files-date-fields.js +3 -7
- package/dist/{utils/merge-permissions.d.ts → database/migrations/20240806A-permissions-policies.d.ts} +4 -1
- package/dist/database/migrations/20240806A-permissions-policies.js +352 -0
- package/dist/database/run-ast/lib/get-db-query.d.ts +4 -0
- package/dist/database/run-ast/lib/get-db-query.js +218 -0
- package/dist/database/run-ast/lib/parse-current-level.d.ts +7 -0
- package/dist/database/run-ast/lib/parse-current-level.js +41 -0
- package/dist/database/run-ast/run-ast.d.ts +7 -0
- package/dist/database/run-ast/run-ast.js +107 -0
- package/dist/database/{run-ast.d.ts → run-ast/types.d.ts} +3 -9
- package/dist/database/run-ast/types.js +1 -0
- package/dist/database/run-ast/utils/apply-case-when.d.ts +16 -0
- package/dist/database/run-ast/utils/apply-case-when.js +27 -0
- package/dist/database/run-ast/utils/apply-parent-filters.d.ts +3 -0
- package/dist/database/run-ast/utils/apply-parent-filters.js +55 -0
- package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +10 -0
- package/dist/database/run-ast/utils/get-column-pre-processor.js +57 -0
- package/dist/database/run-ast/utils/get-field-alias.d.ts +2 -0
- package/dist/database/run-ast/utils/get-field-alias.js +4 -0
- package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +5 -0
- package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +23 -0
- package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +3 -0
- package/dist/database/run-ast/utils/merge-with-parent-items.js +87 -0
- package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +3 -0
- package/dist/database/run-ast/utils/remove-temporary-fields.js +73 -0
- package/dist/database/run-ast/utils/with-preprocess-bindings.d.ts +2 -0
- package/dist/database/run-ast/utils/with-preprocess-bindings.js +14 -0
- package/dist/flows.js +3 -4
- package/dist/middleware/authenticate.js +2 -7
- package/dist/middleware/cache.js +1 -1
- package/dist/middleware/respond.js +1 -1
- package/dist/permissions/cache.d.ts +2 -0
- package/dist/permissions/cache.js +23 -0
- package/dist/permissions/lib/fetch-permissions.d.ts +11 -0
- package/dist/permissions/lib/fetch-permissions.js +56 -0
- package/dist/permissions/lib/fetch-policies.d.ts +14 -0
- package/dist/permissions/lib/fetch-policies.js +43 -0
- package/dist/permissions/lib/fetch-roles-tree.d.ts +3 -0
- package/dist/permissions/lib/fetch-roles-tree.js +28 -0
- package/dist/{services/permissions → permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
- package/dist/permissions/lib/with-app-minimal-permissions.js +10 -0
- package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +7 -0
- package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +56 -0
- package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +3 -0
- package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +16 -0
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +8 -0
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +24 -0
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +9 -0
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +31 -0
- package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +16 -0
- package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +27 -0
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +10 -0
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +23 -0
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +5 -0
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +7 -0
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +5 -0
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +10 -0
- package/dist/permissions/modules/fetch-global-access/types.d.ts +4 -0
- package/dist/permissions/modules/fetch-global-access/types.js +1 -0
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +4 -0
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +27 -0
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +12 -0
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +32 -0
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +4 -0
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +29 -0
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +4 -0
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +49 -0
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +3 -0
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +56 -0
- package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +4 -0
- package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +8 -0
- package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +9 -0
- package/dist/permissions/modules/process-ast/lib/inject-cases.js +93 -0
- package/dist/permissions/modules/process-ast/process-ast.d.ts +9 -0
- package/dist/permissions/modules/process-ast/process-ast.js +39 -0
- package/dist/permissions/modules/process-ast/types.d.ts +18 -0
- package/dist/permissions/modules/process-ast/types.js +1 -0
- package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +7 -0
- package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +12 -0
- package/dist/permissions/modules/process-ast/utils/dedupe-access.js +30 -0
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +15 -0
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +60 -0
- package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +3 -0
- package/dist/permissions/modules/process-ast/utils/find-related-collection.js +9 -0
- package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +3 -0
- package/dist/permissions/modules/process-ast/utils/flatten-filter.js +34 -0
- package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +1 -0
- package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +3 -0
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +5 -0
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +7 -0
- package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +3 -0
- package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +3 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +3 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +16 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +12 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +2 -0
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +28 -0
- package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +5 -0
- package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +12 -0
- package/dist/permissions/modules/process-payload/process-payload.d.ts +13 -0
- package/dist/permissions/modules/process-payload/process-payload.js +77 -0
- package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +12 -0
- package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +11 -0
- package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +9 -0
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +33 -0
- package/dist/permissions/modules/validate-access/validate-access.d.ts +14 -0
- package/dist/permissions/modules/validate-access/validate-access.js +28 -0
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +1 -0
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +8 -0
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +5 -0
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +10 -0
- package/dist/permissions/types.d.ts +6 -0
- package/dist/permissions/types.js +1 -0
- package/dist/permissions/utils/create-default-accountability.d.ts +2 -0
- package/dist/permissions/utils/create-default-accountability.js +11 -0
- package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +8 -0
- package/dist/permissions/utils/extract-required-dynamic-variable-context.js +27 -0
- package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +9 -0
- package/dist/permissions/utils/fetch-dynamic-variable-context.js +43 -0
- package/dist/permissions/utils/filter-policies-by-ip.d.ts +2 -0
- package/dist/permissions/utils/filter-policies-by-ip.js +15 -0
- package/dist/permissions/utils/get-unaliased-field-key.d.ts +5 -0
- package/dist/permissions/utils/get-unaliased-field-key.js +17 -0
- package/dist/permissions/utils/process-permissions.d.ts +7 -0
- package/dist/permissions/utils/process-permissions.js +9 -0
- package/dist/permissions/utils/with-cache.d.ts +10 -0
- package/dist/permissions/utils/with-cache.js +25 -0
- package/dist/server.js +17 -4
- package/dist/services/access.d.ts +10 -0
- package/dist/services/access.js +43 -0
- package/dist/services/activity.js +22 -10
- package/dist/services/assets.d.ts +2 -3
- package/dist/services/assets.js +10 -5
- package/dist/services/authentication.js +18 -18
- package/dist/services/collections.js +18 -17
- package/dist/services/fields.d.ts +0 -1
- package/dist/services/fields.js +54 -25
- package/dist/services/files.js +10 -3
- package/dist/services/graphql/index.d.ts +3 -3
- package/dist/services/graphql/index.js +126 -22
- package/dist/services/graphql/subscription.js +2 -4
- package/dist/services/import-export.d.ts +3 -1
- package/dist/services/import-export.js +67 -9
- package/dist/services/index.d.ts +3 -2
- package/dist/services/index.js +3 -2
- package/dist/services/items.js +115 -44
- package/dist/services/meta.js +60 -23
- package/dist/services/notifications.js +14 -6
- package/dist/services/payload.d.ts +9 -10
- package/dist/services/payload.js +18 -3
- package/dist/services/{permissions/index.d.ts → permissions.d.ts} +5 -7
- package/dist/services/{permissions/index.js → permissions.js} +30 -54
- package/dist/services/policies.d.ts +12 -0
- package/dist/services/policies.js +87 -0
- package/dist/services/relations.d.ts +0 -6
- package/dist/services/relations.js +27 -30
- package/dist/services/roles.d.ts +4 -12
- package/dist/services/roles.js +57 -424
- package/dist/services/shares.d.ts +0 -2
- package/dist/services/shares.js +12 -8
- package/dist/services/specifications.d.ts +2 -2
- package/dist/services/specifications.js +39 -27
- package/dist/services/users.d.ts +1 -5
- package/dist/services/users.js +78 -161
- package/dist/services/utils.js +11 -7
- package/dist/services/versions.d.ts +0 -2
- package/dist/services/versions.js +34 -10
- package/dist/telemetry/lib/get-report.js +2 -2
- package/dist/telemetry/utils/check-user-limits.d.ts +5 -0
- package/dist/telemetry/utils/check-user-limits.js +19 -0
- package/dist/types/ast.d.ts +43 -1
- package/dist/types/database.d.ts +1 -1
- package/dist/types/items.d.ts +11 -0
- package/dist/utils/apply-query.d.ts +11 -7
- package/dist/utils/apply-query.js +69 -11
- package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +19 -0
- package/dist/utils/fetch-user-count/fetch-access-lookup.js +23 -0
- package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +16 -0
- package/dist/utils/fetch-user-count/fetch-access-roles.js +37 -0
- package/dist/utils/fetch-user-count/fetch-active-users.d.ts +6 -0
- package/dist/utils/fetch-user-count/fetch-active-users.js +3 -0
- package/dist/utils/fetch-user-count/fetch-user-count.d.ts +12 -0
- package/dist/utils/fetch-user-count/fetch-user-count.js +64 -0
- package/dist/utils/fetch-user-count/get-user-count-query.d.ts +20 -0
- package/dist/utils/fetch-user-count/get-user-count-query.js +17 -0
- package/dist/utils/get-accountability-for-role.js +16 -25
- package/dist/utils/get-accountability-for-token.js +17 -16
- package/dist/utils/get-address.d.ts +5 -0
- package/dist/utils/get-address.js +13 -0
- package/dist/utils/get-cache-key.d.ts +1 -1
- package/dist/utils/get-cache-key.js +12 -1
- package/dist/utils/get-column.d.ts +2 -1
- package/dist/utils/get-column.js +1 -0
- package/dist/utils/get-service.js +5 -1
- package/dist/utils/reduce-schema.d.ts +4 -6
- package/dist/utils/reduce-schema.js +16 -32
- package/dist/utils/sanitize-schema.d.ts +1 -1
- package/dist/utils/transaction.js +28 -11
- package/dist/utils/validate-user-count-integrity.d.ts +13 -0
- package/dist/utils/validate-user-count-integrity.js +29 -0
- package/dist/websocket/authenticate.d.ts +0 -2
- package/dist/websocket/authenticate.js +0 -12
- package/dist/websocket/controllers/graphql.js +3 -7
- package/dist/websocket/controllers/hooks.js +4 -0
- package/dist/websocket/controllers/rest.js +2 -5
- package/dist/websocket/handlers/subscribe.js +0 -2
- package/dist/websocket/utils/items.d.ts +1 -1
- package/package.json +31 -30
- package/dist/database/run-ast.js +0 -458
- package/dist/middleware/check-ip.d.ts +0 -2
- package/dist/middleware/check-ip.js +0 -37
- package/dist/middleware/get-permissions.d.ts +0 -3
- package/dist/middleware/get-permissions.js +0 -10
- package/dist/services/authorization.d.ts +0 -17
- package/dist/services/authorization.js +0 -456
- package/dist/services/permissions/lib/with-app-minimal-permissions.js +0 -13
- package/dist/telemetry/utils/check-increased-user-limits.d.ts +0 -7
- package/dist/telemetry/utils/check-increased-user-limits.js +0 -25
- package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +0 -6
- package/dist/telemetry/utils/get-role-counts-by-roles.js +0 -27
- package/dist/telemetry/utils/get-role-counts-by-users.d.ts +0 -11
- package/dist/telemetry/utils/get-role-counts-by-users.js +0 -34
- package/dist/telemetry/utils/get-user-count.d.ts +0 -8
- package/dist/telemetry/utils/get-user-count.js +0 -33
- package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +0 -7
- package/dist/telemetry/utils/get-user-counts-by-roles.js +0 -35
- package/dist/utils/get-ast-from-query.d.ts +0 -13
- package/dist/utils/get-ast-from-query.js +0 -297
- package/dist/utils/get-permissions.d.ts +0 -2
- package/dist/utils/get-permissions.js +0 -150
- package/dist/utils/merge-permissions-for-share.d.ts +0 -4
- package/dist/utils/merge-permissions-for-share.js +0 -109
- package/dist/utils/merge-permissions.js +0 -95
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { REGEX_BETWEEN_PARENS } from '@directus/constants';
|
|
2
|
+
import { isEmpty } from 'lodash-es';
|
|
3
|
+
import { fetchPermissions } from '../../../permissions/lib/fetch-permissions.js';
|
|
4
|
+
import { fetchPolicies } from '../../../permissions/lib/fetch-policies.js';
|
|
5
|
+
import { getRelationType } from '../../../utils/get-relation-type.js';
|
|
6
|
+
import { getDeepQuery } from '../utils/get-deep-query.js';
|
|
7
|
+
import { getRelatedCollection } from '../utils/get-related-collection.js';
|
|
8
|
+
import { getRelation } from '../utils/get-relation.js';
|
|
9
|
+
import { convertWildcards } from './convert-wildcards.js';
|
|
10
|
+
export async function parseFields(options, context) {
|
|
11
|
+
let { fields } = options;
|
|
12
|
+
if (!fields)
|
|
13
|
+
return [];
|
|
14
|
+
fields = await convertWildcards({
|
|
15
|
+
fields,
|
|
16
|
+
parentCollection: options.parentCollection,
|
|
17
|
+
query: options.query,
|
|
18
|
+
accountability: options.accountability,
|
|
19
|
+
}, context);
|
|
20
|
+
if (!fields || !Array.isArray(fields))
|
|
21
|
+
return [];
|
|
22
|
+
const children = [];
|
|
23
|
+
const policies = options.accountability && options.accountability.admin === false
|
|
24
|
+
? await fetchPolicies(options.accountability, context)
|
|
25
|
+
: null;
|
|
26
|
+
const relationalStructure = Object.create(null);
|
|
27
|
+
for (const fieldKey of fields) {
|
|
28
|
+
let name = fieldKey;
|
|
29
|
+
if (options.query.alias) {
|
|
30
|
+
// check for field alias (is one of the key)
|
|
31
|
+
if (name in options.query.alias) {
|
|
32
|
+
name = options.query.alias[fieldKey];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const isRelational = name.includes('.') ||
|
|
36
|
+
// We'll always treat top level o2m fields as a related item. This is an alias field, otherwise it won't return
|
|
37
|
+
// anything
|
|
38
|
+
!!context.schema.relations.find((relation) => relation.related_collection === options.parentCollection && relation.meta?.one_field === name);
|
|
39
|
+
if (isRelational) {
|
|
40
|
+
// field is relational
|
|
41
|
+
const parts = fieldKey.split('.');
|
|
42
|
+
let rootField = parts[0];
|
|
43
|
+
let collectionScope = null;
|
|
44
|
+
// a2o related collection scoped field selector `fields=sections.section_id:headings.title`
|
|
45
|
+
if (rootField.includes(':')) {
|
|
46
|
+
const [key, scope] = rootField.split(':');
|
|
47
|
+
rootField = key;
|
|
48
|
+
collectionScope = scope;
|
|
49
|
+
}
|
|
50
|
+
if (rootField in relationalStructure === false) {
|
|
51
|
+
if (collectionScope) {
|
|
52
|
+
relationalStructure[rootField] = { [collectionScope]: [] };
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
relationalStructure[rootField] = [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (parts.length > 1) {
|
|
59
|
+
const childKey = parts.slice(1).join('.');
|
|
60
|
+
if (collectionScope) {
|
|
61
|
+
if (collectionScope in relationalStructure[rootField] === false) {
|
|
62
|
+
relationalStructure[rootField][collectionScope] = [];
|
|
63
|
+
}
|
|
64
|
+
relationalStructure[rootField][collectionScope].push(childKey);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
relationalStructure[rootField].push(childKey);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
if (name.includes('(') && name.includes(')')) {
|
|
73
|
+
const columnName = name.match(REGEX_BETWEEN_PARENS)[1];
|
|
74
|
+
const foundField = context.schema.collections[options.parentCollection].fields[columnName];
|
|
75
|
+
if (foundField && foundField.type === 'alias') {
|
|
76
|
+
const foundRelation = context.schema.relations.find((relation) => relation.related_collection === options.parentCollection && relation.meta?.one_field === columnName);
|
|
77
|
+
if (foundRelation) {
|
|
78
|
+
children.push({
|
|
79
|
+
type: 'functionField',
|
|
80
|
+
name,
|
|
81
|
+
fieldKey,
|
|
82
|
+
query: {},
|
|
83
|
+
relatedCollection: foundRelation.collection,
|
|
84
|
+
whenCase: [],
|
|
85
|
+
cases: [],
|
|
86
|
+
});
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (name.includes(':')) {
|
|
92
|
+
const [key, scope] = name.split(':');
|
|
93
|
+
if (key in relationalStructure === false) {
|
|
94
|
+
relationalStructure[key] = { [scope]: [] };
|
|
95
|
+
}
|
|
96
|
+
else if (scope in relationalStructure[key] === false) {
|
|
97
|
+
relationalStructure[key][scope] = [];
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
children.push({ type: 'field', name, fieldKey, whenCase: [] });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (const [fieldKey, nestedFields] of Object.entries(relationalStructure)) {
|
|
105
|
+
let fieldName = fieldKey;
|
|
106
|
+
if (options.query.alias && fieldKey in options.query.alias) {
|
|
107
|
+
fieldName = options.query.alias[fieldKey];
|
|
108
|
+
}
|
|
109
|
+
const relatedCollection = getRelatedCollection(context.schema, options.parentCollection, fieldName);
|
|
110
|
+
const relation = getRelation(context.schema, options.parentCollection, fieldName);
|
|
111
|
+
if (!relation)
|
|
112
|
+
continue;
|
|
113
|
+
const relationType = getRelationType({
|
|
114
|
+
relation,
|
|
115
|
+
collection: options.parentCollection,
|
|
116
|
+
field: fieldName,
|
|
117
|
+
});
|
|
118
|
+
if (!relationType)
|
|
119
|
+
continue;
|
|
120
|
+
let child = null;
|
|
121
|
+
if (relationType === 'a2o') {
|
|
122
|
+
const allowedCollections = relation.meta.one_allowed_collections;
|
|
123
|
+
child = {
|
|
124
|
+
type: 'a2o',
|
|
125
|
+
names: allowedCollections,
|
|
126
|
+
children: {},
|
|
127
|
+
query: {},
|
|
128
|
+
relatedKey: {},
|
|
129
|
+
parentKey: context.schema.collections[options.parentCollection].primary,
|
|
130
|
+
fieldKey: fieldKey,
|
|
131
|
+
relation: relation,
|
|
132
|
+
cases: {},
|
|
133
|
+
whenCase: [],
|
|
134
|
+
};
|
|
135
|
+
for (const relatedCollection of allowedCollections) {
|
|
136
|
+
child.children[relatedCollection] = await parseFields({
|
|
137
|
+
parentCollection: relatedCollection,
|
|
138
|
+
fields: Array.isArray(nestedFields)
|
|
139
|
+
? nestedFields
|
|
140
|
+
: nestedFields[relatedCollection] || [],
|
|
141
|
+
query: options.query,
|
|
142
|
+
deep: options.deep?.[`${fieldKey}:${relatedCollection}`],
|
|
143
|
+
accountability: options.accountability,
|
|
144
|
+
}, context);
|
|
145
|
+
child.query[relatedCollection] = getDeepQuery(options.deep?.[`${fieldKey}:${relatedCollection}`] || {});
|
|
146
|
+
child.relatedKey[relatedCollection] = context.schema.collections[relatedCollection].primary;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (relatedCollection) {
|
|
150
|
+
if (options.accountability && options.accountability.admin === false && policies) {
|
|
151
|
+
const permissions = await fetchPermissions({
|
|
152
|
+
action: 'read',
|
|
153
|
+
collections: [relatedCollection],
|
|
154
|
+
policies: policies,
|
|
155
|
+
accountability: options.accountability,
|
|
156
|
+
}, context);
|
|
157
|
+
// Skip related collection if no permissions
|
|
158
|
+
if (permissions.length === 0) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// update query alias for children parseFields
|
|
163
|
+
const deepAlias = getDeepQuery(options.deep?.[fieldKey] || {})?.['alias'];
|
|
164
|
+
if (!isEmpty(deepAlias))
|
|
165
|
+
options.query.alias = deepAlias;
|
|
166
|
+
child = {
|
|
167
|
+
type: relationType,
|
|
168
|
+
name: relatedCollection,
|
|
169
|
+
fieldKey: fieldKey,
|
|
170
|
+
parentKey: context.schema.collections[options.parentCollection].primary,
|
|
171
|
+
relatedKey: context.schema.collections[relatedCollection].primary,
|
|
172
|
+
relation: relation,
|
|
173
|
+
query: getDeepQuery(options.deep?.[fieldKey] || {}),
|
|
174
|
+
children: await parseFields({
|
|
175
|
+
parentCollection: relatedCollection,
|
|
176
|
+
fields: nestedFields,
|
|
177
|
+
query: options.query,
|
|
178
|
+
deep: options.deep?.[fieldKey] || {},
|
|
179
|
+
accountability: options.accountability,
|
|
180
|
+
}, context),
|
|
181
|
+
cases: [],
|
|
182
|
+
whenCase: [],
|
|
183
|
+
};
|
|
184
|
+
if (relationType === 'o2m' && !child.query.sort) {
|
|
185
|
+
child.query.sort = [relation.meta?.sort_field || context.schema.collections[relation.collection].primary];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (child) {
|
|
189
|
+
children.push(child);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Deduplicate any children fields that are included both as a regular field, and as a nested m2o field
|
|
193
|
+
const nestedCollectionNodes = children.filter((childNode) => childNode.type !== 'field');
|
|
194
|
+
return children.filter((childNode) => {
|
|
195
|
+
const existsAsNestedRelational = !!nestedCollectionNodes.find((nestedCollectionNode) => childNode.fieldKey === nestedCollectionNode.fieldKey);
|
|
196
|
+
if (childNode.type === 'field' && existsAsNestedRelational)
|
|
197
|
+
return false;
|
|
198
|
+
return true;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert Deep query object to regular query object by ignoring all nested fields and returning the
|
|
3
|
+
* `_` prefixed fields as top level query fields
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
*
|
|
7
|
+
* ```js
|
|
8
|
+
* getDeepQuery({
|
|
9
|
+
* _sort: ['a']
|
|
10
|
+
* });
|
|
11
|
+
* // => { sort: ['a'] }
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function getDeepQuery(query: Record<string, any>): Record<string, any>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { mapKeys, omitBy } from 'lodash-es';
|
|
2
|
+
/**
|
|
3
|
+
* Convert Deep query object to regular query object by ignoring all nested fields and returning the
|
|
4
|
+
* `_` prefixed fields as top level query fields
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* getDeepQuery({
|
|
10
|
+
* _sort: ['a']
|
|
11
|
+
* });
|
|
12
|
+
* // => { sort: ['a'] }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function getDeepQuery(query) {
|
|
16
|
+
return mapKeys(omitBy(query, (_value, key) => key.startsWith('_') === false), (_value, key) => key.substring(1));
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getRelation } from './get-relation.js';
|
|
2
|
+
export function getRelatedCollection(schema, collection, field) {
|
|
3
|
+
const relation = getRelation(schema, collection, field);
|
|
4
|
+
if (!relation)
|
|
5
|
+
return null;
|
|
6
|
+
if (relation.collection === collection && relation.field === field) {
|
|
7
|
+
return relation.related_collection || null;
|
|
8
|
+
}
|
|
9
|
+
if (relation.related_collection === collection && relation.meta?.one_field === field) {
|
|
10
|
+
return relation.collection || null;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function getRelation(schema, collection, field) {
|
|
2
|
+
const relation = schema.relations.find((relation) => {
|
|
3
|
+
return ((relation.collection === collection && relation.field === field) ||
|
|
4
|
+
(relation.related_collection === collection && relation.meta?.one_field === field));
|
|
5
|
+
});
|
|
6
|
+
return relation;
|
|
7
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { Query, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Filter, Query, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
3
|
import { DatabaseHelper } from '../types.js';
|
|
4
4
|
export type FnHelperOptions = {
|
|
5
5
|
type: string | undefined;
|
|
6
6
|
query: Query | undefined;
|
|
7
|
+
cases: Filter[] | undefined;
|
|
7
8
|
originalCollectionName: string | undefined;
|
|
8
9
|
};
|
|
9
10
|
export declare abstract class FnHelper extends DatabaseHelper {
|
|
@@ -28,7 +28,7 @@ export class FnHelper extends DatabaseHelper {
|
|
|
28
28
|
collection: relation.collection,
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
|
-
countQuery = applyFilter(this.knex, this.schema, countQuery, options.query.filter, relation.collection, aliasMap).query;
|
|
31
|
+
countQuery = applyFilter(this.knex, this.schema, countQuery, options.query.filter, relation.collection, aliasMap, options.cases ?? []).query;
|
|
32
32
|
}
|
|
33
33
|
return this.knex.raw('(' + countQuery.toQuery() + ')');
|
|
34
34
|
}
|
|
@@ -6,7 +6,7 @@ export declare class GeometryHelperMSSQL extends GeometryHelper {
|
|
|
6
6
|
isTrue(expression: Knex.Raw): Knex.Raw<any>;
|
|
7
7
|
isFalse(expression: Knex.Raw): Knex.Raw<any>;
|
|
8
8
|
createColumn(table: Knex.CreateTableBuilder, field: RawField | Field): Knex.ColumnBuilder;
|
|
9
|
-
asText(table: string, column: string): Knex.Raw;
|
|
9
|
+
asText(table: string, column: string, alias: string | false): Knex.Raw;
|
|
10
10
|
fromText(text: string): Knex.Raw;
|
|
11
11
|
_intersects(key: string, geojson: GeoJSONGeometry): Knex.Raw;
|
|
12
12
|
_intersects_bbox(key: string, geojson: GeoJSONGeometry): Knex.Raw;
|
|
@@ -12,8 +12,10 @@ export class GeometryHelperMSSQL extends GeometryHelper {
|
|
|
12
12
|
}
|
|
13
13
|
return table.specificType(field.field, 'geometry');
|
|
14
14
|
}
|
|
15
|
-
asText(table, column) {
|
|
16
|
-
|
|
15
|
+
asText(table, column, alias) {
|
|
16
|
+
if (alias)
|
|
17
|
+
return this.knex.raw('??.??.STAsText() as ??', [table, column, alias]);
|
|
18
|
+
return this.knex.raw('??.??.STAsText()', [table, column]);
|
|
17
19
|
}
|
|
18
20
|
fromText(text) {
|
|
19
21
|
return this.knex.raw('geometry::STGeomFromText(?, 4326)', text);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GeometryHelper } from '../types.js';
|
|
2
2
|
export class GeometryHelperMySQL extends GeometryHelper {
|
|
3
3
|
collect(table, column) {
|
|
4
|
-
return this.knex.raw(`concat('geometrycollection(', group_concat(? separator ', '), ')'`, this.asText(table, column));
|
|
4
|
+
return this.knex.raw(`concat('geometrycollection(', group_concat(? separator ', '), ')'`, this.asText(table, column, column));
|
|
5
5
|
}
|
|
6
6
|
fromText(text) {
|
|
7
7
|
return this.knex.raw('st_geomfromtext(?)', text);
|
|
@@ -6,7 +6,7 @@ export declare class GeometryHelperOracle extends GeometryHelper {
|
|
|
6
6
|
isTrue(expression: Knex.Raw): Knex.Raw<any>;
|
|
7
7
|
isFalse(expression: Knex.Raw): Knex.Raw<any>;
|
|
8
8
|
createColumn(table: Knex.CreateTableBuilder, field: RawField | Field): Knex.ColumnBuilder;
|
|
9
|
-
asText(table: string, column: string): Knex.Raw;
|
|
9
|
+
asText(table: string, column: string, alias: string | false): Knex.Raw;
|
|
10
10
|
asGeoJSON(table: string, column: string): Knex.Raw;
|
|
11
11
|
fromText(text: string): Knex.Raw;
|
|
12
12
|
_intersects(key: string, geojson: GeoJSONGeometry): Knex.Raw;
|
|
@@ -12,8 +12,10 @@ export class GeometryHelperOracle extends GeometryHelper {
|
|
|
12
12
|
}
|
|
13
13
|
return table.specificType(field.field, 'sdo_geometry');
|
|
14
14
|
}
|
|
15
|
-
asText(table, column) {
|
|
16
|
-
|
|
15
|
+
asText(table, column, alias) {
|
|
16
|
+
if (alias)
|
|
17
|
+
return this.knex.raw('sdo_util.to_wktgeometry(??.??) as ??', [table, column, alias]);
|
|
18
|
+
return this.knex.raw('sdo_util.to_wktgeometry(??.??)', [table, column]);
|
|
17
19
|
}
|
|
18
20
|
asGeoJSON(table, column) {
|
|
19
21
|
return this.knex.raw('sdo_util.to_geojson(??.??) as ??', [table, column, column]);
|
|
@@ -30,6 +32,6 @@ export class GeometryHelperOracle extends GeometryHelper {
|
|
|
30
32
|
return this.knex.raw(`sdo_overlapbdyintersect(sdo_geom.sdo_mbr(??), sdo_geom.sdo_mbr(?))`, [key, geometry]);
|
|
31
33
|
}
|
|
32
34
|
collect(table, column) {
|
|
33
|
-
return this.knex.raw(`concat('geometrycollection(', listagg(?, ', '), ')'`, this.asText(table, column));
|
|
35
|
+
return this.knex.raw(`concat('geometrycollection(', listagg(?, ', '), ')'`, this.asText(table, column, column));
|
|
34
36
|
}
|
|
35
37
|
}
|
|
@@ -7,7 +7,7 @@ export declare abstract class GeometryHelper extends DatabaseHelper {
|
|
|
7
7
|
isTrue(expression: Knex.Raw): Knex.Raw<any>;
|
|
8
8
|
isFalse(expression: Knex.Raw): Knex.Raw<any>;
|
|
9
9
|
createColumn(table: Knex.CreateTableBuilder, field: RawField | Field): Knex.ColumnBuilder;
|
|
10
|
-
asText(table: string, column: string): Knex.Raw;
|
|
10
|
+
asText(table: string, column: string, alias: string | false): Knex.Raw;
|
|
11
11
|
fromText(text: string): Knex.Raw;
|
|
12
12
|
fromGeoJSON(geojson: GeoJSONGeometry): Knex.Raw;
|
|
13
13
|
_intersects(key: string, geojson: GeoJSONGeometry): Knex.Raw;
|
|
@@ -14,8 +14,10 @@ export class GeometryHelper extends DatabaseHelper {
|
|
|
14
14
|
const type = field.type.split('.')[1] ?? 'geometry';
|
|
15
15
|
return table.specificType(field.field, type);
|
|
16
16
|
}
|
|
17
|
-
asText(table, column) {
|
|
18
|
-
|
|
17
|
+
asText(table, column, alias) {
|
|
18
|
+
if (alias)
|
|
19
|
+
return this.knex.raw('st_astext(??.??) as ??', [table, column, alias]);
|
|
20
|
+
return this.knex.raw('st_astext(??.??)', [table, column]);
|
|
19
21
|
}
|
|
20
22
|
fromText(text) {
|
|
21
23
|
return this.knex.raw('st_geomfromtext(?, 4326)', text);
|
|
@@ -8,10 +8,10 @@ import * as sequenceHelpers from './sequence/index.js';
|
|
|
8
8
|
import * as numberHelpers from './number/index.js';
|
|
9
9
|
export declare function getHelpers(database: Knex): {
|
|
10
10
|
date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
|
|
11
|
-
st: geometryHelpers.
|
|
12
|
-
schema: schemaHelpers.
|
|
11
|
+
st: geometryHelpers.postgres | geometryHelpers.mssql | geometryHelpers.mysql | geometryHelpers.sqlite | geometryHelpers.oracle | geometryHelpers.redshift;
|
|
12
|
+
schema: schemaHelpers.cockroachdb | schemaHelpers.mssql | schemaHelpers.mysql | schemaHelpers.postgres | schemaHelpers.sqlite | schemaHelpers.oracle | schemaHelpers.redshift;
|
|
13
13
|
sequence: sequenceHelpers.mysql | sequenceHelpers.postgres;
|
|
14
14
|
number: numberHelpers.cockroachdb | numberHelpers.mssql | numberHelpers.postgres | numberHelpers.sqlite | numberHelpers.oracle;
|
|
15
15
|
};
|
|
16
|
-
export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.
|
|
16
|
+
export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.postgres | fnHelpers.mssql | fnHelpers.mysql | fnHelpers.sqlite | fnHelpers.oracle;
|
|
17
17
|
export type Helpers = ReturnType<typeof getHelpers>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { KNEX_TYPES } from '@directus/constants';
|
|
2
|
-
import type { Options } from '../types.js';
|
|
2
|
+
import type { Options, Sql } from '../types.js';
|
|
3
3
|
import { SchemaHelper } from '../types.js';
|
|
4
4
|
export declare class SchemaHelperCockroachDb extends SchemaHelper {
|
|
5
5
|
changeToType(table: string, column: string, type: (typeof KNEX_TYPES)[number], options?: Options): Promise<void>;
|
|
6
6
|
constraintName(existingName: string): string;
|
|
7
7
|
getDatabaseSize(): Promise<number | null>;
|
|
8
|
+
preprocessBindings(queryParams: Sql): Sql;
|
|
8
9
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SchemaHelper } from '../types.js';
|
|
2
2
|
import { useEnv } from '@directus/env';
|
|
3
|
+
import { preprocessBindings } from '../utils/preprocess-bindings.js';
|
|
3
4
|
const env = useEnv();
|
|
4
5
|
export class SchemaHelperCockroachDb extends SchemaHelper {
|
|
5
6
|
async changeToType(table, column, type, options = {}) {
|
|
@@ -27,4 +28,7 @@ export class SchemaHelperCockroachDb extends SchemaHelper {
|
|
|
27
28
|
return null;
|
|
28
29
|
}
|
|
29
30
|
}
|
|
31
|
+
preprocessBindings(queryParams) {
|
|
32
|
+
return preprocessBindings(queryParams, { format: (index) => `$${index + 1}` });
|
|
33
|
+
}
|
|
30
34
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
|
-
import { SchemaHelper } from '../types.js';
|
|
2
|
+
import { SchemaHelper, type Sql } from '../types.js';
|
|
3
3
|
export declare class SchemaHelperMSSQL extends SchemaHelper {
|
|
4
4
|
applyLimit(rootQuery: Knex.QueryBuilder, limit: number): void;
|
|
5
5
|
applyOffset(rootQuery: Knex.QueryBuilder, offset: number): void;
|
|
6
6
|
formatUUID(uuid: string): string;
|
|
7
7
|
getDatabaseSize(): Promise<number | null>;
|
|
8
|
+
preprocessBindings(queryParams: Sql): Sql;
|
|
8
9
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SchemaHelper } from '../types.js';
|
|
2
|
+
import { preprocessBindings } from '../utils/preprocess-bindings.js';
|
|
2
3
|
export class SchemaHelperMSSQL extends SchemaHelper {
|
|
3
4
|
applyLimit(rootQuery, limit) {
|
|
4
5
|
// The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries,
|
|
@@ -26,4 +27,7 @@ export class SchemaHelperMSSQL extends SchemaHelper {
|
|
|
26
27
|
return null;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
30
|
+
preprocessBindings(queryParams) {
|
|
31
|
+
return preprocessBindings(queryParams, { format: (index) => `@p${index}` });
|
|
32
|
+
}
|
|
29
33
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { KNEX_TYPES } from '@directus/constants';
|
|
2
2
|
import type { Field, Relation, Type } from '@directus/types';
|
|
3
|
-
import type { Options } from '../types.js';
|
|
3
|
+
import type { Options, Sql } from '../types.js';
|
|
4
4
|
import { SchemaHelper } from '../types.js';
|
|
5
5
|
export declare class SchemaHelperOracle extends SchemaHelper {
|
|
6
6
|
changeToType(table: string, column: string, type: (typeof KNEX_TYPES)[number], options?: Options): Promise<void>;
|
|
@@ -8,4 +8,5 @@ export declare class SchemaHelperOracle extends SchemaHelper {
|
|
|
8
8
|
preRelationChange(relation: Partial<Relation>): void;
|
|
9
9
|
processFieldType(field: Field): Type;
|
|
10
10
|
getDatabaseSize(): Promise<number | null>;
|
|
11
|
+
preprocessBindings(queryParams: Sql): Sql;
|
|
11
12
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SchemaHelper } from '../types.js';
|
|
2
|
+
import { preprocessBindings } from '../utils/preprocess-bindings.js';
|
|
2
3
|
export class SchemaHelperOracle extends SchemaHelper {
|
|
3
4
|
async changeToType(table, column, type, options = {}) {
|
|
4
5
|
await this.changeToTypeByCopy(table, column, type, options);
|
|
@@ -38,4 +39,7 @@ export class SchemaHelperOracle extends SchemaHelper {
|
|
|
38
39
|
return null;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
preprocessBindings(queryParams) {
|
|
43
|
+
return preprocessBindings(queryParams, { format: (index) => `:${index + 1}` });
|
|
44
|
+
}
|
|
41
45
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { SchemaHelper } from '../types.js';
|
|
3
|
+
import { preprocessBindings } from '../utils/preprocess-bindings.js';
|
|
3
4
|
const env = useEnv();
|
|
4
5
|
export class SchemaHelperPostgres extends SchemaHelper {
|
|
5
6
|
async getDatabaseSize() {
|
|
@@ -11,4 +12,7 @@ export class SchemaHelperPostgres extends SchemaHelper {
|
|
|
11
12
|
return null;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
15
|
+
preprocessBindings(queryParams) {
|
|
16
|
+
return preprocessBindings(queryParams, { format: (index) => `$${index + 1}` });
|
|
17
|
+
}
|
|
14
18
|
}
|
|
@@ -8,6 +8,10 @@ export type Options = {
|
|
|
8
8
|
default?: any;
|
|
9
9
|
length?: number;
|
|
10
10
|
};
|
|
11
|
+
export type Sql = {
|
|
12
|
+
sql: string;
|
|
13
|
+
bindings: readonly Knex.Value[];
|
|
14
|
+
};
|
|
11
15
|
export declare abstract class SchemaHelper extends DatabaseHelper {
|
|
12
16
|
isOneOfClients(clients: DatabaseClient[]): boolean;
|
|
13
17
|
changeNullable(table: string, column: string, nullable: boolean): Promise<void>;
|
|
@@ -27,4 +31,5 @@ export declare abstract class SchemaHelper extends DatabaseHelper {
|
|
|
27
31
|
* @returns Size of the database in bytes
|
|
28
32
|
*/
|
|
29
33
|
getDatabaseSize(): Promise<number | null>;
|
|
34
|
+
preprocessBindings(queryParams: Sql): Sql;
|
|
30
35
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Sql } from '../types.js';
|
|
2
|
+
export type PreprocessBindingsOptions = {
|
|
3
|
+
format(index: number): string;
|
|
4
|
+
};
|
|
5
|
+
export declare function preprocessBindings(queryParams: (Partial<Sql> & Pick<Sql, 'sql'>) | string, options: PreprocessBindingsOptions): {
|
|
6
|
+
sql: string;
|
|
7
|
+
bindings: import("knex").Knex.Value[];
|
|
8
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isString } from 'lodash-es';
|
|
2
|
+
export function preprocessBindings(queryParams, options) {
|
|
3
|
+
const query = { bindings: [], ...(isString(queryParams) ? { sql: queryParams } : queryParams) };
|
|
4
|
+
const bindingIndices = new Array(query.bindings.length);
|
|
5
|
+
for (let i = 0; i < query.bindings.length; i++) {
|
|
6
|
+
const binding = query.bindings[i];
|
|
7
|
+
const prevIndex = query.bindings.findIndex((b, j) => j < i && b === binding);
|
|
8
|
+
if (prevIndex !== -1) {
|
|
9
|
+
bindingIndices[i] = prevIndex;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
bindingIndices[i] = i;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
let matchIndex = 0;
|
|
16
|
+
let currentBindingIndex = 0;
|
|
17
|
+
const sql = query.sql.replace(/(\\*)(\?)/g, function (_, escapes) {
|
|
18
|
+
if (escapes.length % 2) {
|
|
19
|
+
// Return an escaped question mark, so it stays escaped
|
|
20
|
+
return `${'\\'.repeat(escapes.length)}?`;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const bindingIndex = bindingIndices[matchIndex] === matchIndex ? currentBindingIndex++ : bindingIndices[matchIndex];
|
|
24
|
+
matchIndex++;
|
|
25
|
+
return options.format(bindingIndex);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const bindings = query.bindings.filter((_, i) => bindingIndices[i] === i);
|
|
29
|
+
return { ...query, sql, bindings };
|
|
30
|
+
}
|
package/dist/database/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { createInspector } from '@directus/schema';
|
|
3
|
+
import { isObject } from '@directus/utils';
|
|
3
4
|
import fse from 'fs-extra';
|
|
4
5
|
import knex from 'knex';
|
|
5
6
|
import { merge } from 'lodash-es';
|
|
@@ -106,6 +107,10 @@ export function getDatabase() {
|
|
|
106
107
|
};
|
|
107
108
|
}
|
|
108
109
|
if (client === 'mysql') {
|
|
110
|
+
// Remove the conflicting `filename` option, defined by default in the Docker Image
|
|
111
|
+
if (isObject(knexConfig.connection))
|
|
112
|
+
delete knexConfig.connection['filename'];
|
|
113
|
+
Object.assign(knexConfig, { client: 'mysql2' });
|
|
109
114
|
poolConfig.afterCreate = async (conn, callback) => {
|
|
110
115
|
logger.trace('Retrieving database version');
|
|
111
116
|
const run = promisify(conn.query.bind(conn));
|
|
@@ -189,7 +194,7 @@ export async function validateDatabaseConnection(database) {
|
|
|
189
194
|
export function getDatabaseClient(database) {
|
|
190
195
|
database = database ?? getDatabase();
|
|
191
196
|
switch (database.client.constructor.name) {
|
|
192
|
-
case '
|
|
197
|
+
case 'Client_MySQL2':
|
|
193
198
|
return 'mysql';
|
|
194
199
|
case 'Client_PG':
|
|
195
200
|
return 'postgres';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createInspector } from '@directus/schema';
|
|
2
2
|
import { useLogger } from '../../logger/index.js';
|
|
3
|
+
import { getDatabaseClient } from '../index.js';
|
|
3
4
|
/**
|
|
4
5
|
* Things to keep in mind:
|
|
5
6
|
*
|
|
@@ -99,7 +100,7 @@ export async function up(knex) {
|
|
|
99
100
|
* MySQL won't delete the index when you drop the foreign key constraint. Gotta make
|
|
100
101
|
* sure to clean those up as well
|
|
101
102
|
*/
|
|
102
|
-
if (knex
|
|
103
|
+
if (getDatabaseClient(knex) === 'mysql') {
|
|
103
104
|
try {
|
|
104
105
|
await knex.schema.alterTable(update.table, (table) => {
|
|
105
106
|
// Knex uses a default convention for index names: `table_column_type`
|
|
@@ -140,7 +141,7 @@ export async function down(knex) {
|
|
|
140
141
|
* MySQL won't delete the index when you drop the foreign key constraint. Gotta make
|
|
141
142
|
* sure to clean those up as well
|
|
142
143
|
*/
|
|
143
|
-
if (knex
|
|
144
|
+
if (getDatabaseClient(knex) === 'mysql') {
|
|
144
145
|
try {
|
|
145
146
|
await knex.schema.alterTable(update.table, (table) => {
|
|
146
147
|
// Knex uses a default convention for index names: `table_column_type`
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { createInspector } from '@directus/schema';
|
|
2
2
|
import { useLogger } from '../../logger/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { getDatabaseClient } from '../index.js';
|
|
4
4
|
export async function up(knex) {
|
|
5
|
-
const
|
|
6
|
-
const isMysql = helper.isOneOfClients(['mysql']);
|
|
5
|
+
const isMysql = getDatabaseClient(knex) === 'mysql';
|
|
7
6
|
if (isMysql) {
|
|
8
7
|
await dropConstraint(knex);
|
|
9
8
|
}
|
|
@@ -16,8 +15,7 @@ export async function up(knex) {
|
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
17
|
export async function down(knex) {
|
|
19
|
-
const
|
|
20
|
-
const isMysql = helper.isOneOfClients(['mysql']);
|
|
18
|
+
const isMysql = getDatabaseClient(knex) === 'mysql';
|
|
21
19
|
if (isMysql) {
|
|
22
20
|
await dropConstraint(knex);
|
|
23
21
|
}
|