velocious 1.0.643 → 1.0.645
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/README.md +21 -10
- package/build/authorization/ability.js +36 -13
- package/build/background-jobs/adapter.js +9 -0
- package/build/background-jobs/main.js +35 -10
- package/build/background-jobs/store.js +217 -38
- package/build/background-jobs/types.js +15 -0
- package/build/configuration-types.js +1 -1
- package/build/configuration.js +45 -9
- package/build/database/drivers/base-column.js +8 -0
- package/build/database/drivers/mssql/sql/update.js +1 -3
- package/build/database/drivers/mysql/sql/update.js +1 -3
- package/build/database/drivers/pgsql/column.js +22 -0
- package/build/database/drivers/pgsql/sql/update.js +1 -3
- package/build/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/build/database/drivers/sqlite/sql/update.js +1 -3
- package/build/database/query/index.js +2 -2
- package/build/database/query/model-class-query.js +26 -10
- package/build/database/query/preloader/has-many.js +4 -1
- package/build/database/query/query-data.js +2 -1
- package/build/database/query/update-base.js +14 -0
- package/build/database/query/with-count.js +3 -1
- package/build/database/record/acts-as-list.js +37 -13
- package/build/database/record/attachments/store.js +336 -26
- package/build/database/record/auditing.js +6 -3
- package/build/database/record/index.js +197 -42
- package/build/database/record/instance-relationships/belongs-to.js +2 -1
- package/build/database/record/validators/uniqueness.js +8 -5
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/build/frontend-model-controller.js +136 -53
- package/build/frontend-model-resource/base-resource.js +87 -37
- package/build/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/build/frontend-models/base.js +307 -59
- package/build/frontend-models/preloader.js +6 -3
- package/build/frontend-models/query.js +15 -6
- package/build/frontend-models/resource-definition.js +21 -2
- package/build/frontend-models/use-destroyed-event.js +1 -1
- package/build/frontend-models/use-model-class-event.js +2 -2
- package/build/frontend-models/use-updated-event.js +1 -1
- package/build/frontend-models/websocket-channel.js +256 -93
- package/build/frontend-models/websocket-publishers.js +218 -18
- package/build/http-server/client/websocket-session.js +13 -1
- package/build/http-server/websocket-channel.js +15 -2
- package/build/src/authorization/ability.d.ts +9 -3
- package/build/src/authorization/ability.d.ts.map +1 -1
- package/build/src/authorization/ability.js +33 -14
- package/build/src/background-jobs/adapter.d.ts +6 -0
- package/build/src/background-jobs/adapter.d.ts.map +1 -1
- package/build/src/background-jobs/adapter.js +9 -1
- package/build/src/background-jobs/main.d.ts +6 -0
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +34 -11
- package/build/src/background-jobs/store.d.ts +66 -12
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +191 -35
- package/build/src/background-jobs/types.d.ts +55 -0
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +16 -1
- package/build/src/configuration-types.d.ts +3 -3
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +2 -2
- package/build/src/configuration.d.ts +18 -8
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +38 -7
- package/build/src/database/drivers/base-column.d.ts +5 -0
- package/build/src/database/drivers/base-column.d.ts.map +1 -1
- package/build/src/database/drivers/base-column.js +8 -1
- package/build/src/database/drivers/mssql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/sql/update.js +2 -4
- package/build/src/database/drivers/mysql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/update.js +2 -4
- package/build/src/database/drivers/pgsql/column.d.ts +5 -0
- package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/column.js +19 -1
- package/build/src/database/drivers/pgsql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/sql/update.js +2 -4
- package/build/src/database/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/alter-table.js +6 -2
- package/build/src/database/drivers/sqlite/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/update.js +2 -4
- package/build/src/database/query/index.d.ts +4 -4
- package/build/src/database/query/index.d.ts.map +1 -1
- package/build/src/database/query/index.js +3 -3
- package/build/src/database/query/model-class-query.d.ts +8 -2
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +25 -11
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +4 -2
- package/build/src/database/query/query-data.d.ts.map +1 -1
- package/build/src/database/query/query-data.js +3 -2
- package/build/src/database/query/update-base.d.ts +7 -0
- package/build/src/database/query/update-base.d.ts.map +1 -1
- package/build/src/database/query/update-base.js +13 -1
- package/build/src/database/query/with-count.d.ts.map +1 -1
- package/build/src/database/query/with-count.js +3 -2
- package/build/src/database/record/acts-as-list.d.ts.map +1 -1
- package/build/src/database/record/acts-as-list.js +35 -14
- package/build/src/database/record/attachments/store.d.ts +67 -0
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +301 -25
- package/build/src/database/record/auditing.d.ts.map +1 -1
- package/build/src/database/record/auditing.js +7 -4
- package/build/src/database/record/index.d.ts +78 -26
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +171 -42
- package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +3 -2
- package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
- package/build/src/database/record/validators/uniqueness.js +8 -6
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +4 -4
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -12
- package/build/src/frontend-model-controller.d.ts +29 -8
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +122 -51
- package/build/src/frontend-model-resource/base-resource.d.ts +51 -24
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +83 -37
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts +45 -26
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/velocious-attachment-resource.js +89 -31
- package/build/src/frontend-models/base.d.ts +53 -20
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +275 -59
- package/build/src/frontend-models/preloader.d.ts.map +1 -1
- package/build/src/frontend-models/preloader.js +6 -4
- package/build/src/frontend-models/query.d.ts +2 -2
- package/build/src/frontend-models/query.d.ts.map +1 -1
- package/build/src/frontend-models/query.js +14 -7
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +20 -3
- package/build/src/frontend-models/use-destroyed-event.d.ts +1 -1
- package/build/src/frontend-models/use-destroyed-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-destroyed-event.js +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-model-class-event.js +3 -3
- package/build/src/frontend-models/use-updated-event.d.ts +1 -1
- package/build/src/frontend-models/use-updated-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-updated-event.js +2 -2
- package/build/src/frontend-models/websocket-channel.d.ts +60 -41
- package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-channel.js +225 -85
- package/build/src/frontend-models/websocket-publishers.d.ts +9 -0
- package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-publishers.js +190 -19
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +11 -2
- package/build/src/http-server/websocket-channel.d.ts +20 -8
- package/build/src/http-server/websocket-channel.d.ts.map +1 -1
- package/build/src/http-server/websocket-channel.js +15 -3
- package/build/src/sync/assets/cache.d.ts +288 -0
- package/build/src/sync/assets/cache.d.ts.map +1 -0
- package/build/src/sync/assets/cache.js +883 -0
- package/build/src/sync/assets/types.d.ts +199 -0
- package/build/src/sync/assets/types.d.ts.map +1 -0
- package/build/src/sync/assets/types.js +56 -0
- package/build/src/sync/sync-api-client.d.ts.map +1 -1
- package/build/src/sync/sync-api-client.js +4 -3
- package/build/src/sync/sync-client.d.ts.map +1 -1
- package/build/src/sync/sync-client.js +3 -2
- package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
- package/build/src/sync/sync-envelope-replay-service.js +6 -5
- package/build/src/sync/sync-publisher.d.ts.map +1 -1
- package/build/src/sync/sync-publisher.js +4 -3
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts +2 -2
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts.map +1 -1
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.js +3 -3
- package/build/src/testing/test-profiler.d.ts +4 -4
- package/build/src/testing/test-profiler.d.ts.map +1 -1
- package/build/src/testing/test-profiler.js +3 -3
- package/build/src/testing/test-runner.d.ts +279 -117
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +503 -933
- package/build/src/testing/test.d.ts +11 -72
- package/build/src/testing/test.d.ts.map +1 -1
- package/build/src/testing/test.js +131 -284
- package/build/src/testing/velocious-attempt-executor.d.ts +54 -0
- package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -0
- package/build/src/testing/velocious-attempt-executor.js +408 -0
- package/build/src/testing/velocious-runner-reporter.d.ts +67 -0
- package/build/src/testing/velocious-runner-reporter.d.ts.map +1 -0
- package/build/src/testing/velocious-runner-reporter.js +240 -0
- package/build/src/testing/velocious-suite-hook-executor.d.ts +19 -0
- package/build/src/testing/velocious-suite-hook-executor.d.ts.map +1 -0
- package/build/src/testing/velocious-suite-hook-executor.js +41 -0
- package/build/src/testing/velocious-test-arguments.d.ts +39 -0
- package/build/src/testing/velocious-test-arguments.d.ts.map +1 -0
- package/build/src/testing/velocious-test-arguments.js +55 -0
- package/build/src/utils/model-primary-key.d.ts +73 -0
- package/build/src/utils/model-primary-key.d.ts.map +1 -0
- package/build/src/utils/model-primary-key.js +144 -0
- package/build/src/utils/sha256-bytes-hex.d.ts +7 -0
- package/build/src/utils/sha256-bytes-hex.d.ts.map +1 -0
- package/build/src/utils/sha256-bytes-hex.js +117 -0
- package/build/src/utils/sha256-hex.d.ts +2 -4
- package/build/src/utils/sha256-hex.d.ts.map +1 -1
- package/build/src/utils/sha256-hex.js +6 -120
- package/build/sync/assets/cache.js +998 -0
- package/build/sync/assets/types.js +62 -0
- package/build/sync/sync-api-client.js +3 -2
- package/build/sync/sync-client.js +2 -1
- package/build/sync/sync-envelope-replay-service.js +5 -4
- package/build/sync/sync-publisher.js +3 -2
- package/build/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/build/testing/test-profiler.js +2 -2
- package/build/testing/test-runner.js +496 -990
- package/build/testing/test.js +164 -306
- package/build/testing/velocious-attempt-executor.js +441 -0
- package/build/testing/velocious-runner-reporter.js +264 -0
- package/build/testing/velocious-suite-hook-executor.js +43 -0
- package/build/testing/velocious-test-arguments.js +64 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/utils/model-primary-key.js +171 -0
- package/build/utils/sha256-bytes-hex.js +132 -0
- package/build/utils/sha256-hex.js +7 -135
- package/package.json +5 -4
- package/src/authorization/ability.js +36 -13
- package/src/background-jobs/adapter.js +9 -0
- package/src/background-jobs/main.js +35 -10
- package/src/background-jobs/store.js +217 -38
- package/src/background-jobs/types.js +15 -0
- package/src/configuration-types.js +1 -1
- package/src/configuration.js +45 -9
- package/src/database/drivers/base-column.js +8 -0
- package/src/database/drivers/mssql/sql/update.js +1 -3
- package/src/database/drivers/mysql/sql/update.js +1 -3
- package/src/database/drivers/pgsql/column.js +22 -0
- package/src/database/drivers/pgsql/sql/update.js +1 -3
- package/src/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/src/database/drivers/sqlite/sql/update.js +1 -3
- package/src/database/query/index.js +2 -2
- package/src/database/query/model-class-query.js +26 -10
- package/src/database/query/preloader/has-many.js +4 -1
- package/src/database/query/query-data.js +2 -1
- package/src/database/query/update-base.js +14 -0
- package/src/database/query/with-count.js +3 -1
- package/src/database/record/acts-as-list.js +37 -13
- package/src/database/record/attachments/store.js +336 -26
- package/src/database/record/auditing.js +6 -3
- package/src/database/record/index.js +197 -42
- package/src/database/record/instance-relationships/belongs-to.js +2 -1
- package/src/database/record/validators/uniqueness.js +8 -5
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/src/frontend-model-controller.js +136 -53
- package/src/frontend-model-resource/base-resource.js +87 -37
- package/src/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/src/frontend-models/base.js +307 -59
- package/src/frontend-models/preloader.js +6 -3
- package/src/frontend-models/query.js +15 -6
- package/src/frontend-models/resource-definition.js +21 -2
- package/src/frontend-models/use-destroyed-event.js +1 -1
- package/src/frontend-models/use-model-class-event.js +2 -2
- package/src/frontend-models/use-updated-event.js +1 -1
- package/src/frontend-models/websocket-channel.js +256 -93
- package/src/frontend-models/websocket-publishers.js +218 -18
- package/src/http-server/client/websocket-session.js +13 -1
- package/src/http-server/websocket-channel.js +15 -2
- package/src/sync/assets/cache.js +998 -0
- package/src/sync/assets/types.js +62 -0
- package/src/sync/sync-api-client.js +3 -2
- package/src/sync/sync-client.js +2 -1
- package/src/sync/sync-envelope-replay-service.js +5 -4
- package/src/sync/sync-publisher.js +3 -2
- package/src/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/src/testing/test-profiler.js +2 -2
- package/src/testing/test-runner.js +496 -990
- package/src/testing/test.js +164 -306
- package/src/testing/velocious-attempt-executor.js +441 -0
- package/src/testing/velocious-runner-reporter.js +264 -0
- package/src/testing/velocious-suite-hook-executor.js +43 -0
- package/src/testing/velocious-test-arguments.js +64 -0
- package/src/utils/model-primary-key.js +171 -0
- package/src/utils/sha256-bytes-hex.js +132 -0
- package/src/utils/sha256-hex.js +7 -135
- package/build/src/testing/testing-package-adapter.d.ts +0 -9
- package/build/src/testing/testing-package-adapter.d.ts.map +0 -1
- package/build/src/testing/testing-package-adapter.js +0 -69
- package/build/testing/testing-package-adapter.js +0 -76
- package/src/testing/testing-package-adapter.js +0 -76
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import {createHash} from "node:crypto"
|
|
3
4
|
import UUID from "pure-uuid"
|
|
4
5
|
import TableData from "../../table-data/index.js"
|
|
6
|
+
import TableIndex from "../../table-data/table-index.js"
|
|
7
|
+
import {modelPrimaryKeyCacheKey} from "../../../utils/model-primary-key.js"
|
|
5
8
|
import normalizeRecordAttachmentInput from "./normalize-input.js"
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* AttachmentDriverConstructor type.
|
|
9
12
|
* @typedef {import("../../../configuration-types.js").AttachmentDriverConstructor} AttachmentDriverConstructor */
|
|
10
13
|
const ATTACHMENTS_TABLE = "velocious_attachments"
|
|
14
|
+
const ATTACHMENT_OWNER_INDEX_NAME = "index_velocious_attachments_on_record_type_and_record_id_digest"
|
|
15
|
+
const ATTACHMENT_RECORD_ID_DIGEST_LENGTH = 64
|
|
16
|
+
const ATTACHMENT_RECORD_ID_DIGEST_MIGRATION_BATCH_SIZE = 100
|
|
17
|
+
const ATTACHMENT_SCHEMA_LOCK_NAME = "velocious-attachments-schema"
|
|
11
18
|
|
|
12
19
|
/**
|
|
13
20
|
* Stores by configuration.
|
|
@@ -22,6 +29,45 @@ function generateUUID() {
|
|
|
22
29
|
return new UUID(4).format()
|
|
23
30
|
}
|
|
24
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Returns the canonical stored owner identity for a model attachment.
|
|
34
|
+
* @param {import("../index.js").default} model - Attachment owner.
|
|
35
|
+
* @returns {string} - Canonical owner identity.
|
|
36
|
+
*/
|
|
37
|
+
function attachmentRecordId(model) {
|
|
38
|
+
return modelPrimaryKeyCacheKey(model.getModelClass().primaryKey(), model.id())
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns a bounded digest for indexed attachment owner lookups.
|
|
43
|
+
* @param {string} recordId - Canonical attachment owner identity.
|
|
44
|
+
* @returns {string} - SHA-256 digest.
|
|
45
|
+
*/
|
|
46
|
+
function attachmentRecordIdDigest(recordId) {
|
|
47
|
+
return createHash("sha256").update(recordId).digest("hex")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Builds collision-safe attachment owner lookup conditions.
|
|
52
|
+
* @param {object} args - Owner lookup values.
|
|
53
|
+
* @param {string} [args.name] - Optional attachment name.
|
|
54
|
+
* @param {string} args.recordId - Canonical owner identity.
|
|
55
|
+
* @param {string} args.recordType - Owner model name.
|
|
56
|
+
* @returns {Record<string, string>} - Indexed digest and canonical identity conditions.
|
|
57
|
+
*/
|
|
58
|
+
function attachmentOwnerConditions({name, recordId, recordType}) {
|
|
59
|
+
/** @type {Record<string, string>} */
|
|
60
|
+
const conditions = {
|
|
61
|
+
record_id: recordId,
|
|
62
|
+
record_id_digest: attachmentRecordIdDigest(recordId),
|
|
63
|
+
record_type: recordType
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (name !== undefined) conditions.name = name
|
|
67
|
+
|
|
68
|
+
return conditions
|
|
69
|
+
}
|
|
70
|
+
|
|
25
71
|
/**
|
|
26
72
|
* Runs store key for model.
|
|
27
73
|
* @param {import("../index.js").default} model - Model instance.
|
|
@@ -36,12 +82,30 @@ function storeKeyForModel(model) {
|
|
|
36
82
|
}
|
|
37
83
|
|
|
38
84
|
/**
|
|
39
|
-
*
|
|
40
|
-
* @param {import("../index.js").default}
|
|
41
|
-
* @
|
|
85
|
+
* Returns the physical store key for an already-selected model connection.
|
|
86
|
+
* @param {typeof import("../index.js").default} modelClass - Model class.
|
|
87
|
+
* @param {import("../../drivers/base.js").default} connection - Selected physical connection.
|
|
88
|
+
* @returns {string} - Physical store key.
|
|
42
89
|
*/
|
|
43
|
-
|
|
44
|
-
const
|
|
90
|
+
function storeKeyForModelClass(modelClass, connection) {
|
|
91
|
+
const databaseIdentifier = modelClass.getDatabaseIdentifier()
|
|
92
|
+
const reuseKey = modelClass
|
|
93
|
+
._getConfiguration()
|
|
94
|
+
.getDatabasePool(databaseIdentifier)
|
|
95
|
+
.getConnectionConfigurationReuseKey(connection)
|
|
96
|
+
|
|
97
|
+
return `${databaseIdentifier}:${reuseKey}`
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the shared attachment store for one configured database identity.
|
|
102
|
+
* @param {object} args - Store identity.
|
|
103
|
+
* @param {import("../../../configuration.js").default} args.configuration - Owning configuration.
|
|
104
|
+
* @param {string} args.databaseIdentifier - Logical database identifier.
|
|
105
|
+
* @param {string} args.storeKey - Physical store key.
|
|
106
|
+
* @returns {RecordAttachmentsStore} - Shared store instance.
|
|
107
|
+
*/
|
|
108
|
+
function recordAttachmentsStore({configuration, databaseIdentifier, storeKey}) {
|
|
45
109
|
let storesByDatabaseIdentifier = storesByConfiguration.get(configuration)
|
|
46
110
|
|
|
47
111
|
if (!storesByDatabaseIdentifier) {
|
|
@@ -49,19 +113,46 @@ export function recordAttachmentsStoreForModel(model) {
|
|
|
49
113
|
storesByConfiguration.set(configuration, storesByDatabaseIdentifier)
|
|
50
114
|
}
|
|
51
115
|
|
|
52
|
-
|
|
53
|
-
let store = storesByDatabaseIdentifier.get(key)
|
|
116
|
+
let store = storesByDatabaseIdentifier.get(storeKey)
|
|
54
117
|
|
|
55
118
|
if (store) return store
|
|
56
119
|
|
|
57
|
-
store = new RecordAttachmentsStore({
|
|
58
|
-
|
|
59
|
-
|
|
120
|
+
store = new RecordAttachmentsStore({configuration, databaseIdentifier})
|
|
121
|
+
storesByDatabaseIdentifier.set(storeKey, store)
|
|
122
|
+
|
|
123
|
+
return store
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Returns the attachment store used before a model-level transaction starts.
|
|
128
|
+
* @param {typeof import("../index.js").default} modelClass - Model class opening the transaction.
|
|
129
|
+
* @param {import("../../drivers/base.js").default} connection - Selected physical connection.
|
|
130
|
+
* @returns {RecordAttachmentsStore} - Store instance.
|
|
131
|
+
*/
|
|
132
|
+
export function recordAttachmentsStoreForModelClass(modelClass, connection) {
|
|
133
|
+
const databaseIdentifier = modelClass.getDatabaseIdentifier()
|
|
134
|
+
|
|
135
|
+
return recordAttachmentsStore({
|
|
136
|
+
configuration: modelClass._getConfiguration(),
|
|
137
|
+
databaseIdentifier,
|
|
138
|
+
storeKey: storeKeyForModelClass(modelClass, connection)
|
|
60
139
|
})
|
|
140
|
+
}
|
|
61
141
|
|
|
62
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Runs the recordAttachmentsStoreForModel helper.
|
|
144
|
+
* @param {import("../index.js").default} model - Model instance.
|
|
145
|
+
* @returns {RecordAttachmentsStore} - Store instance.
|
|
146
|
+
*/
|
|
147
|
+
export function recordAttachmentsStoreForModel(model) {
|
|
148
|
+
const configuration = model._getConfiguration()
|
|
149
|
+
const databaseIdentifier = model.databaseOperation()?.databaseIdentifier() || model.getModelClass().getDatabaseIdentifier()
|
|
63
150
|
|
|
64
|
-
return
|
|
151
|
+
return recordAttachmentsStore({
|
|
152
|
+
configuration,
|
|
153
|
+
databaseIdentifier,
|
|
154
|
+
storeKey: storeKeyForModel(model)
|
|
155
|
+
})
|
|
65
156
|
}
|
|
66
157
|
|
|
67
158
|
/**
|
|
@@ -78,6 +169,9 @@ export default class RecordAttachmentsStore {
|
|
|
78
169
|
this.configuration = configuration
|
|
79
170
|
this.databaseIdentifier = databaseIdentifier
|
|
80
171
|
this._readyPromise = null
|
|
172
|
+
this._schemaUpgradePromise = null
|
|
173
|
+
/** @type {number | undefined} */
|
|
174
|
+
this._schemaReadyGeneration = undefined
|
|
81
175
|
this._driverColumnsAvailable = false
|
|
82
176
|
this._contentBase64Nullable = true
|
|
83
177
|
/**
|
|
@@ -120,8 +214,6 @@ export default class RecordAttachmentsStore {
|
|
|
120
214
|
* @returns {Promise<void>} - Resolves when schema is ready.
|
|
121
215
|
*/
|
|
122
216
|
async ensureSchema(db) {
|
|
123
|
-
db.clearSchemaCache()
|
|
124
|
-
|
|
125
217
|
if (await db.tableExists(ATTACHMENTS_TABLE)) {
|
|
126
218
|
await this.ensureAttachmentStoreSchema({db})
|
|
127
219
|
return
|
|
@@ -131,7 +223,8 @@ export default class RecordAttachmentsStore {
|
|
|
131
223
|
|
|
132
224
|
table.string("id", {null: false, primaryKey: true})
|
|
133
225
|
table.string("record_type", {null: false, index: true})
|
|
134
|
-
table.
|
|
226
|
+
table.text("record_id", {null: false})
|
|
227
|
+
table.string("record_id_digest", {maxLength: ATTACHMENT_RECORD_ID_DIGEST_LENGTH, null: false})
|
|
135
228
|
table.string("name", {null: false, index: true})
|
|
136
229
|
table.integer("position", {null: false})
|
|
137
230
|
table.string("filename", {null: false})
|
|
@@ -142,10 +235,12 @@ export default class RecordAttachmentsStore {
|
|
|
142
235
|
table.text("content_base64", {null: true})
|
|
143
236
|
table.bigint("created_at_ms", {null: false})
|
|
144
237
|
table.bigint("updated_at_ms", {null: false})
|
|
238
|
+
table.addIndex(new TableIndex(["record_type", "record_id_digest"], {name: ATTACHMENT_OWNER_INDEX_NAME}))
|
|
145
239
|
|
|
146
240
|
await db.createTable(table)
|
|
147
241
|
this._driverColumnsAvailable = true
|
|
148
242
|
this._contentBase64Nullable = true
|
|
243
|
+
this._schemaReadyGeneration = this._schemaCacheGeneration(db)
|
|
149
244
|
}
|
|
150
245
|
|
|
151
246
|
/**
|
|
@@ -198,7 +293,7 @@ export default class RecordAttachmentsStore {
|
|
|
198
293
|
if (persistenceFailed) {
|
|
199
294
|
throw new AggregateError(
|
|
200
295
|
[persistenceError, closeError],
|
|
201
|
-
`Attachment persistence and path-source close both failed for ${model.getModelClass().getModelName()}#${
|
|
296
|
+
`Attachment persistence and path-source close both failed for ${model.getModelClass().getModelName()}#${attachmentRecordId(model)} (${name})`,
|
|
202
297
|
{cause: closeError}
|
|
203
298
|
)
|
|
204
299
|
}
|
|
@@ -242,7 +337,7 @@ export default class RecordAttachmentsStore {
|
|
|
242
337
|
const attachmentDriverName = this._attachmentDriverNameFor({model, name})
|
|
243
338
|
const now = Date.now()
|
|
244
339
|
const recordType = model.getModelClass().getModelName()
|
|
245
|
-
const recordId =
|
|
340
|
+
const recordId = attachmentRecordId(model)
|
|
246
341
|
const attachmentId = generateUUID()
|
|
247
342
|
/**
|
|
248
343
|
* Written storage key.
|
|
@@ -270,7 +365,7 @@ export default class RecordAttachmentsStore {
|
|
|
270
365
|
const existingRows = await db
|
|
271
366
|
.newQuery()
|
|
272
367
|
.from(ATTACHMENTS_TABLE)
|
|
273
|
-
.where({name,
|
|
368
|
+
.where(attachmentOwnerConditions({name, recordId, recordType}))
|
|
274
369
|
.results()
|
|
275
370
|
|
|
276
371
|
for (const existingRow of existingRows) {
|
|
@@ -278,7 +373,7 @@ export default class RecordAttachmentsStore {
|
|
|
278
373
|
}
|
|
279
374
|
|
|
280
375
|
await db.delete({
|
|
281
|
-
conditions: {name,
|
|
376
|
+
conditions: attachmentOwnerConditions({name, recordId, recordType}),
|
|
282
377
|
tableName: ATTACHMENTS_TABLE
|
|
283
378
|
})
|
|
284
379
|
}
|
|
@@ -297,6 +392,7 @@ export default class RecordAttachmentsStore {
|
|
|
297
392
|
name,
|
|
298
393
|
position,
|
|
299
394
|
record_id: recordId,
|
|
395
|
+
record_id_digest: attachmentRecordIdDigest(recordId),
|
|
300
396
|
record_type: recordType,
|
|
301
397
|
updated_at_ms: now
|
|
302
398
|
}
|
|
@@ -354,14 +450,92 @@ export default class RecordAttachmentsStore {
|
|
|
354
450
|
* @returns {Promise<void>} - Resolves when schema columns are ensured.
|
|
355
451
|
*/
|
|
356
452
|
async ensureAttachmentStoreSchema({db}) {
|
|
453
|
+
if (this._schemaReadyGeneration === this._schemaCacheGeneration(db)) return
|
|
454
|
+
if (this._schemaUpgradePromise) return await this._schemaUpgradePromise
|
|
455
|
+
|
|
456
|
+
this._schemaUpgradePromise = (async () => {
|
|
457
|
+
const acquired = await db.acquireAdvisoryLock(ATTACHMENT_SCHEMA_LOCK_NAME)
|
|
458
|
+
|
|
459
|
+
if (!acquired) throw new Error(`Failed to acquire attachment schema lock ${ATTACHMENT_SCHEMA_LOCK_NAME}`)
|
|
460
|
+
|
|
461
|
+
try {
|
|
462
|
+
if (!await db.tableExists(ATTACHMENTS_TABLE)) return
|
|
463
|
+
|
|
464
|
+
await this._ensureAttachmentStoreSchema({db})
|
|
465
|
+
} finally {
|
|
466
|
+
await db.releaseAdvisoryLock(ATTACHMENT_SCHEMA_LOCK_NAME)
|
|
467
|
+
}
|
|
468
|
+
})()
|
|
469
|
+
|
|
470
|
+
try {
|
|
471
|
+
await this._schemaUpgradePromise
|
|
472
|
+
this._schemaReadyGeneration = this._schemaCacheGeneration(db)
|
|
473
|
+
} finally {
|
|
474
|
+
this._schemaUpgradePromise = null
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Returns the schema-cache generation for the connection's physical database.
|
|
480
|
+
* @param {import("../../../database/drivers/base.js").default} db - DB connection.
|
|
481
|
+
* @returns {number} - Current schema-cache generation.
|
|
482
|
+
*/
|
|
483
|
+
_schemaCacheGeneration(db) {
|
|
484
|
+
const pool = this.configuration.getDatabasePool(this.databaseIdentifier)
|
|
485
|
+
const reuseKey = pool.getConnectionConfigurationReuseKey(db)
|
|
486
|
+
|
|
487
|
+
return this.configuration.schemaCacheGenerationForReuseKey(reuseKey)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Ensures attachment columns and indexes after schema-upgrade serialization is acquired.
|
|
492
|
+
* @param {object} args - Options.
|
|
493
|
+
* @param {import("../../../database/drivers/base.js").default} args.db - DB connection.
|
|
494
|
+
* @returns {Promise<void>} - Resolves when schema columns are ensured.
|
|
495
|
+
*/
|
|
496
|
+
async _ensureAttachmentStoreSchema({db}) {
|
|
357
497
|
const table = await db.getTableByNameOrFail(ATTACHMENTS_TABLE)
|
|
358
498
|
const columns = await table.getColumns()
|
|
359
499
|
const hasDriverColumn = columns.some((column) => column.getName() === "driver")
|
|
360
500
|
const hasStorageKeyColumn = columns.some((column) => column.getName() === "storage_key")
|
|
361
501
|
const contentBase64Column = columns.find((column) => column.getName() === "content_base64")
|
|
502
|
+
const recordIdColumn = columns.find((column) => column.getName() === "record_id")
|
|
503
|
+
const recordIdDigestColumn = columns.find((column) => column.getName() === "record_id_digest")
|
|
362
504
|
const alterTable = new TableData(ATTACHMENTS_TABLE)
|
|
363
505
|
let shouldAlter = false
|
|
364
506
|
|
|
507
|
+
if (!recordIdColumn) throw new Error(`${ATTACHMENTS_TABLE}.record_id is missing`)
|
|
508
|
+
|
|
509
|
+
const recordIdMaxLength = recordIdColumn.getMaxLength()
|
|
510
|
+
|
|
511
|
+
if (typeof recordIdMaxLength === "number" && recordIdMaxLength > 0) {
|
|
512
|
+
for (const index of await recordIdColumn.getIndexes()) {
|
|
513
|
+
if (index.isPrimaryKey()) continue
|
|
514
|
+
|
|
515
|
+
const indexName = index.getName()
|
|
516
|
+
|
|
517
|
+
if (!indexName) throw new Error(`Expected a name for ${ATTACHMENTS_TABLE}.record_id index`)
|
|
518
|
+
|
|
519
|
+
for (const sql of await db.removeIndexSQLs({name: indexName, tableName: ATTACHMENTS_TABLE})) {
|
|
520
|
+
await db.query(sql)
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
db.clearSchemaCache()
|
|
525
|
+
const recordIdAlterTable = new TableData(ATTACHMENTS_TABLE)
|
|
526
|
+
|
|
527
|
+
recordIdAlterTable.text("record_id", {
|
|
528
|
+
isNewColumn: false,
|
|
529
|
+
null: db.getType() === "pgsql" ? undefined : false
|
|
530
|
+
})
|
|
531
|
+
|
|
532
|
+
for (const sql of await db.alterTableSQLs(recordIdAlterTable)) {
|
|
533
|
+
await db.query(sql)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
db.clearSchemaCache()
|
|
537
|
+
}
|
|
538
|
+
|
|
365
539
|
if (!hasDriverColumn) {
|
|
366
540
|
alterTable.string("driver", {null: true})
|
|
367
541
|
shouldAlter = true
|
|
@@ -372,18 +546,113 @@ export default class RecordAttachmentsStore {
|
|
|
372
546
|
shouldAlter = true
|
|
373
547
|
}
|
|
374
548
|
|
|
549
|
+
if (!recordIdDigestColumn) {
|
|
550
|
+
alterTable.string("record_id_digest", {maxLength: ATTACHMENT_RECORD_ID_DIGEST_LENGTH, null: true})
|
|
551
|
+
shouldAlter = true
|
|
552
|
+
}
|
|
553
|
+
|
|
375
554
|
if (shouldAlter) {
|
|
376
555
|
const alterTableSQLs = await db.alterTableSQLs(alterTable)
|
|
377
556
|
|
|
378
557
|
for (const sql of alterTableSQLs) {
|
|
379
558
|
await db.query(sql)
|
|
380
559
|
}
|
|
560
|
+
|
|
561
|
+
db.clearSchemaCache()
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (!recordIdDigestColumn || recordIdDigestColumn.getNull()) {
|
|
565
|
+
await this.backfillAttachmentRecordIdDigests(db)
|
|
566
|
+
await this.ensureAttachmentRecordIdDigestNotNull(db)
|
|
381
567
|
}
|
|
382
568
|
|
|
569
|
+
await this.ensureAttachmentOwnerIndex(db)
|
|
570
|
+
|
|
383
571
|
this._driverColumnsAvailable = true
|
|
384
572
|
this._contentBase64Nullable = contentBase64Column ? contentBase64Column.getNull() : true
|
|
385
573
|
}
|
|
386
574
|
|
|
575
|
+
/**
|
|
576
|
+
* Backfills bounded attachment owner digests in small batches.
|
|
577
|
+
* @param {import("../../../database/drivers/base.js").default} db - Database connection.
|
|
578
|
+
* @returns {Promise<void>} - Resolves when every existing row has a digest.
|
|
579
|
+
*/
|
|
580
|
+
async backfillAttachmentRecordIdDigests(db) {
|
|
581
|
+
while (true) {
|
|
582
|
+
const rows = await db
|
|
583
|
+
.newQuery()
|
|
584
|
+
.from(ATTACHMENTS_TABLE)
|
|
585
|
+
.where({record_id_digest: null})
|
|
586
|
+
.limit(ATTACHMENT_RECORD_ID_DIGEST_MIGRATION_BATCH_SIZE)
|
|
587
|
+
.results()
|
|
588
|
+
|
|
589
|
+
for (const row of rows) {
|
|
590
|
+
if (typeof row.id !== "string" || typeof row.record_id !== "string") {
|
|
591
|
+
throw new Error(`Expected canonical attachment identity strings while backfilling ${ATTACHMENTS_TABLE}`)
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
await db.update({
|
|
595
|
+
conditions: {id: row.id},
|
|
596
|
+
data: {record_id_digest: attachmentRecordIdDigest(row.record_id)},
|
|
597
|
+
tableName: ATTACHMENTS_TABLE
|
|
598
|
+
})
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (rows.length < ATTACHMENT_RECORD_ID_DIGEST_MIGRATION_BATCH_SIZE) return
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Makes the backfilled attachment owner digest required.
|
|
607
|
+
* @param {import("../../../database/drivers/base.js").default} db - Database connection.
|
|
608
|
+
* @returns {Promise<void>} - Resolves when the digest column is non-nullable.
|
|
609
|
+
*/
|
|
610
|
+
async ensureAttachmentRecordIdDigestNotNull(db) {
|
|
611
|
+
db.clearSchemaCache()
|
|
612
|
+
const table = await db.getTableByNameOrFail(ATTACHMENTS_TABLE)
|
|
613
|
+
const recordIdDigestColumn = await table.getColumnByNameOrFail("record_id_digest")
|
|
614
|
+
|
|
615
|
+
if (!recordIdDigestColumn.getNull()) return
|
|
616
|
+
|
|
617
|
+
const alterTable = new TableData(ATTACHMENTS_TABLE)
|
|
618
|
+
|
|
619
|
+
alterTable.string("record_id_digest", {
|
|
620
|
+
isNewColumn: false,
|
|
621
|
+
maxLength: ATTACHMENT_RECORD_ID_DIGEST_LENGTH,
|
|
622
|
+
null: false
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
for (const sql of await db.alterTableSQLs(alterTable)) await db.query(sql)
|
|
626
|
+
|
|
627
|
+
db.clearSchemaCache()
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Ensures attachment owner queries retain a bounded composite index.
|
|
632
|
+
* @param {import("../../../database/drivers/base.js").default} db - Database connection.
|
|
633
|
+
* @returns {Promise<void>} - Resolves when the owner index exists.
|
|
634
|
+
*/
|
|
635
|
+
async ensureAttachmentOwnerIndex(db) {
|
|
636
|
+
const table = await db.getTableByNameOrFail(ATTACHMENTS_TABLE)
|
|
637
|
+
const indexes = await table.getIndexes()
|
|
638
|
+
const ownerIndex = indexes.find((index) => {
|
|
639
|
+
const columnNames = index.getColumnNames()
|
|
640
|
+
|
|
641
|
+
return columnNames.length === 2 && columnNames[0] === "record_type" && columnNames[1] === "record_id_digest"
|
|
642
|
+
})
|
|
643
|
+
|
|
644
|
+
if (ownerIndex) return
|
|
645
|
+
|
|
646
|
+
for (const sql of await db.createIndexSQLs({
|
|
647
|
+
columns: ["record_type", "record_id_digest"],
|
|
648
|
+
ifNotExists: true,
|
|
649
|
+
name: ATTACHMENT_OWNER_INDEX_NAME,
|
|
650
|
+
tableName: ATTACHMENTS_TABLE
|
|
651
|
+
})) await db.query(sql)
|
|
652
|
+
|
|
653
|
+
db.clearSchemaCache()
|
|
654
|
+
}
|
|
655
|
+
|
|
387
656
|
/**
|
|
388
657
|
* Runs read attachment row.
|
|
389
658
|
* @param {object} args - Options.
|
|
@@ -453,11 +722,11 @@ export default class RecordAttachmentsStore {
|
|
|
453
722
|
|
|
454
723
|
return await this._withDb(async (db) => {
|
|
455
724
|
const recordType = model.getModelClass().getModelName()
|
|
456
|
-
const recordId =
|
|
725
|
+
const recordId = attachmentRecordId(model)
|
|
457
726
|
let query = db
|
|
458
727
|
.newQuery()
|
|
459
728
|
.from(ATTACHMENTS_TABLE)
|
|
460
|
-
.where({name,
|
|
729
|
+
.where(attachmentOwnerConditions({name, recordId, recordType}))
|
|
461
730
|
.order("position ASC")
|
|
462
731
|
.order("created_at_ms DESC")
|
|
463
732
|
.limit(1)
|
|
@@ -484,11 +753,11 @@ export default class RecordAttachmentsStore {
|
|
|
484
753
|
|
|
485
754
|
return await this._withDb(async (db) => {
|
|
486
755
|
const recordType = model.getModelClass().getModelName()
|
|
487
|
-
const recordId =
|
|
756
|
+
const recordId = attachmentRecordId(model)
|
|
488
757
|
const query = db
|
|
489
758
|
.newQuery()
|
|
490
759
|
.from(ATTACHMENTS_TABLE)
|
|
491
|
-
.where({name,
|
|
760
|
+
.where(attachmentOwnerConditions({name, recordId, recordType}))
|
|
492
761
|
.order("position ASC")
|
|
493
762
|
.order("created_at_ms ASC")
|
|
494
763
|
|
|
@@ -496,6 +765,47 @@ export default class RecordAttachmentsStore {
|
|
|
496
765
|
}, model)
|
|
497
766
|
}
|
|
498
767
|
|
|
768
|
+
/**
|
|
769
|
+
* Prepares attachment schema before a record transaction can migrate ownership.
|
|
770
|
+
* @param {object} args - Options.
|
|
771
|
+
* @param {import("../../drivers/base.js").default} args.connection - Record-owning database connection.
|
|
772
|
+
* @returns {Promise<void>} - Resolves when existing attachment schema is current.
|
|
773
|
+
*/
|
|
774
|
+
async prepareRecordIdentityMigration({connection}) {
|
|
775
|
+
await this.ensureAttachmentStoreSchema({db: connection})
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Moves every attachment row to a record's new primary-key identity.
|
|
780
|
+
* @param {object} args - Options.
|
|
781
|
+
* @param {import("../../drivers/base.js").default} args.connection - Transaction-owning database connection.
|
|
782
|
+
* @param {import("../index.js").default} args.model - Attachment owner after the key change.
|
|
783
|
+
* @param {import("../../../utils/model-primary-key.js").ModelPrimaryKeyValue} args.nextIdentity - New owner identity.
|
|
784
|
+
* @param {import("../../../utils/model-primary-key.js").ModelPrimaryKeyValue} args.previousIdentity - Persisted owner identity.
|
|
785
|
+
* @returns {Promise<void>} - Resolves after ownership is migrated.
|
|
786
|
+
*/
|
|
787
|
+
async migrateRecordIdentity({connection, model, nextIdentity, previousIdentity}) {
|
|
788
|
+
const primaryKey = model.getModelClass().primaryKey()
|
|
789
|
+
const nextRecordId = modelPrimaryKeyCacheKey(primaryKey, nextIdentity)
|
|
790
|
+
const previousRecordId = modelPrimaryKeyCacheKey(primaryKey, previousIdentity)
|
|
791
|
+
|
|
792
|
+
if (nextRecordId === previousRecordId) return
|
|
793
|
+
|
|
794
|
+
if (!await connection.tableExists(ATTACHMENTS_TABLE)) return
|
|
795
|
+
|
|
796
|
+
await connection.update({
|
|
797
|
+
conditions: attachmentOwnerConditions({
|
|
798
|
+
recordId: previousRecordId,
|
|
799
|
+
recordType: model.getModelClass().getModelName()
|
|
800
|
+
}),
|
|
801
|
+
data: {
|
|
802
|
+
record_id: nextRecordId,
|
|
803
|
+
record_id_digest: attachmentRecordIdDigest(nextRecordId)
|
|
804
|
+
},
|
|
805
|
+
tableName: ATTACHMENTS_TABLE
|
|
806
|
+
})
|
|
807
|
+
}
|
|
808
|
+
|
|
499
809
|
/**
|
|
500
810
|
* Runs delete attachment row storage.
|
|
501
811
|
* @param {object} args - Options.
|
|
@@ -535,12 +845,12 @@ export default class RecordAttachmentsStore {
|
|
|
535
845
|
|
|
536
846
|
return await this._withDb(async (db) => {
|
|
537
847
|
const recordType = model.getModelClass().getModelName()
|
|
538
|
-
const recordId =
|
|
848
|
+
const recordId = attachmentRecordId(model)
|
|
539
849
|
/** @type {Array<Record<string, ReturnType<typeof JSON.parse>>>} */
|
|
540
850
|
const rows = await db
|
|
541
851
|
.newQuery()
|
|
542
852
|
.from(ATTACHMENTS_TABLE)
|
|
543
|
-
.where({name,
|
|
853
|
+
.where(attachmentOwnerConditions({name, recordId, recordType}))
|
|
544
854
|
.results()
|
|
545
855
|
|
|
546
856
|
// Refuse to purge when any row's driver cannot delete its backing storage:
|
|
@@ -730,7 +1040,7 @@ export default class RecordAttachmentsStore {
|
|
|
730
1040
|
const query = db
|
|
731
1041
|
.newQuery()
|
|
732
1042
|
.from(ATTACHMENTS_TABLE)
|
|
733
|
-
.where({name,
|
|
1043
|
+
.where(attachmentOwnerConditions({name, recordId, recordType}))
|
|
734
1044
|
.order("position DESC")
|
|
735
1045
|
.limit(1)
|
|
736
1046
|
const rows = await query.results()
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import UUID from "pure-uuid"
|
|
4
4
|
import HasManyRelationship from "./relationships/has-many.js"
|
|
5
|
+
import {scalarModelPrimaryKey, scalarModelPrimaryKeyValue} from "../../utils/model-primary-key.js"
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Global audit event bus matching ActiveRecordAuditable::Events.
|
|
@@ -648,6 +649,7 @@ async function createAuditWithCurrentConnection(record, args, modelClass) {
|
|
|
648
649
|
const auditedChanges = args.auditedChanges === undefined ? null : args.auditedChanges
|
|
649
650
|
const params = args.params === undefined ? null : args.params
|
|
650
651
|
const currentDate = new Date()
|
|
652
|
+
const recordId = scalarModelPrimaryKeyValue(record.id(), `Auditing for ${modelClass.name}`)
|
|
651
653
|
|
|
652
654
|
const auditActionId = await findOrCreateLookupId({
|
|
653
655
|
columnName: "action",
|
|
@@ -667,7 +669,7 @@ async function createAuditWithCurrentConnection(record, args, modelClass) {
|
|
|
667
669
|
tableName: tableData.tableName,
|
|
668
670
|
data: {
|
|
669
671
|
id: auditId,
|
|
670
|
-
[`${modelKey}_id`]:
|
|
672
|
+
[`${modelKey}_id`]: recordId,
|
|
671
673
|
audit_action_id: auditActionId,
|
|
672
674
|
audited_changes: auditedChanges,
|
|
673
675
|
params,
|
|
@@ -691,7 +693,7 @@ async function createAuditWithCurrentConnection(record, args, modelClass) {
|
|
|
691
693
|
id: auditId,
|
|
692
694
|
audit_action_id: auditActionId,
|
|
693
695
|
audit_auditable_type_id: auditAuditableTypeId,
|
|
694
|
-
auditable_id:
|
|
696
|
+
auditable_id: recordId,
|
|
695
697
|
auditable_type: modelClass.getModelName(),
|
|
696
698
|
audited_changes: auditedChanges,
|
|
697
699
|
params,
|
|
@@ -836,7 +838,8 @@ function withoutAudit(modelClass, action) {
|
|
|
836
838
|
const tableData = auditTableDataByModel.get(modelClass)?.get(databaseIdentity)
|
|
837
839
|
const modelTableSql = db.quoteTable(modelClass.tableName())
|
|
838
840
|
const auditActionsTableSql = db.quoteTable("audit_actions")
|
|
839
|
-
const
|
|
841
|
+
const primaryKey = scalarModelPrimaryKey(modelClass.primaryKey(), `withoutAudit for ${modelClass.name}`)
|
|
842
|
+
const modelPrimaryKeySql = `${modelTableSql}.${db.quoteColumn(primaryKey)}`
|
|
840
843
|
const auditActionsIdSql = `${auditActionsTableSql}.${db.quoteColumn("id")}`
|
|
841
844
|
const auditActionsActionSql = `${auditActionsTableSql}.${db.quoteColumn("action")}`
|
|
842
845
|
|