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
|
@@ -21,6 +21,7 @@ import { normalizeDateStringForWrite } from "./database/datetime-storage.js"
|
|
|
21
21
|
import VelociousError from "./velocious-error.js"
|
|
22
22
|
import isDate from "./utils/is-date.js"
|
|
23
23
|
import isPlainObject from "./utils/plain-object.js"
|
|
24
|
+
import {compositeModelPrimaryKeyCohortSql, modelPrimaryKeyCacheKey, modelPrimaryKeyConditions, modelPrimaryKeyValueFromCacheKey, readModelPrimaryKeyValue, scalarModelPrimaryKey} from "./utils/model-primary-key.js"
|
|
24
25
|
import {RansackQueryError, normalizeRansackGroup, parseRansackSort} from "./utils/ransack.js"
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -80,6 +81,8 @@ import {RansackQueryError, normalizeRansackGroup, parseRansackSort} from "./util
|
|
|
80
81
|
* @returns {void}
|
|
81
82
|
*/
|
|
82
83
|
|
|
84
|
+
const ATTACHMENT_OWNER_KEY = "__attachmentOwner"
|
|
85
|
+
|
|
83
86
|
/**
|
|
84
87
|
* Runs normalize frontend model preload.
|
|
85
88
|
* @param {import("./database/query/index.js").NestedPreloadRecord | string | string[] | boolean | undefined | null} preload - Preload shorthand.
|
|
@@ -821,6 +824,10 @@ export default class FrontendModelController extends Controller {
|
|
|
821
824
|
|
|
822
825
|
if (!frontendModelResource) return null
|
|
823
826
|
|
|
827
|
+
if (this.frontendModelResourceModelClass(frontendModelResource) === modelClass) {
|
|
828
|
+
return frontendModelResource
|
|
829
|
+
}
|
|
830
|
+
|
|
824
831
|
return this.frontendModelResourceConfigurationForBackendProjectModelName({
|
|
825
832
|
backendProject: frontendModelResource.backendProject,
|
|
826
833
|
modelName: modelClass.getModelName()
|
|
@@ -1047,10 +1054,10 @@ export default class FrontendModelController extends Controller {
|
|
|
1047
1054
|
|
|
1048
1055
|
/**
|
|
1049
1056
|
* Runs frontend model primary key.
|
|
1050
|
-
* @returns {
|
|
1057
|
+
* @returns {import("./utils/model-primary-key.js").ModelPrimaryKeyDefinition} - Frontend model primary key.
|
|
1051
1058
|
*/
|
|
1052
1059
|
frontendModelPrimaryKey() {
|
|
1053
|
-
return this.
|
|
1060
|
+
return this.frontendModelResourceInstance().primaryKey()
|
|
1054
1061
|
}
|
|
1055
1062
|
|
|
1056
1063
|
/**
|
|
@@ -1086,41 +1093,95 @@ export default class FrontendModelController extends Controller {
|
|
|
1086
1093
|
/**
|
|
1087
1094
|
* Runs frontend model ability authorized query.
|
|
1088
1095
|
* @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "attachmentList" | "download" | "url"} action - Frontend action.
|
|
1096
|
+
* @param {{ruleQueryFactory?: () => import("./database/query/model-class-query.js").default<typeof import("./database/record/index.js").default>}} [options] - Authorization query options.
|
|
1089
1097
|
* @returns {import("./database/query/model-class-query.js").default<typeof import("./database/record/index.js").default>} - Authorized query for the action.
|
|
1090
1098
|
*/
|
|
1091
|
-
frontendModelAbilityAuthorizedQuery(action) {
|
|
1099
|
+
frontendModelAbilityAuthorizedQuery(action, {ruleQueryFactory} = {}) {
|
|
1092
1100
|
const abilityAction = this.frontendModelAbilityAction(action)
|
|
1101
|
+
const modelClass = this.frontendModelClass()
|
|
1102
|
+
|
|
1103
|
+
if (!ruleQueryFactory) return modelClass.accessibleFor(abilityAction, this.currentAbility())
|
|
1104
|
+
|
|
1105
|
+
const ability = this.currentAbility()
|
|
1093
1106
|
|
|
1094
|
-
|
|
1107
|
+
if (!ability) {
|
|
1108
|
+
throw new Error(`No ability in context for ${modelClass.name}. Configure an ability resolver on the request`)
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
return ability.applyToQuery({
|
|
1112
|
+
action: abilityAction,
|
|
1113
|
+
modelClass,
|
|
1114
|
+
query: modelClass._newQuery(),
|
|
1115
|
+
ruleQueryFactory
|
|
1116
|
+
})
|
|
1095
1117
|
}
|
|
1096
1118
|
|
|
1097
1119
|
/**
|
|
1098
1120
|
* Runs frontend model authorized query.
|
|
1099
1121
|
* @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "attachmentList" | "download" | "url"} action - Frontend action.
|
|
1122
|
+
* @param {{ruleQueryFactory?: () => import("./database/query/model-class-query.js").default<typeof import("./database/record/index.js").default>}} [options] - Authorization query options.
|
|
1100
1123
|
* @returns {import("./database/query/model-class-query.js").default<typeof import("./database/record/index.js").default>} - Authorized query for the action.
|
|
1101
1124
|
*/
|
|
1102
|
-
frontendModelAuthorizedQuery(action) {
|
|
1125
|
+
frontendModelAuthorizedQuery(action, options = {}) {
|
|
1103
1126
|
const resource = this.frontendModelResourceInstance()
|
|
1104
1127
|
|
|
1105
1128
|
if (resource.authorizedQuery !== FrontendModelBaseResource.prototype.authorizedQuery) {
|
|
1106
|
-
return resource.authorizedQuery(action)
|
|
1129
|
+
return resource.authorizedQuery(action, options)
|
|
1107
1130
|
}
|
|
1108
1131
|
|
|
1109
|
-
return this.frontendModelAbilityAuthorizedQuery(action)
|
|
1132
|
+
return this.frontendModelAbilityAuthorizedQuery(action, options)
|
|
1110
1133
|
}
|
|
1111
1134
|
|
|
1112
1135
|
/**
|
|
1113
1136
|
* Runs frontend model primary key value.
|
|
1114
1137
|
* @param {import("./database/record/index.js").default} model - Model instance.
|
|
1115
|
-
* @returns {
|
|
1138
|
+
* @returns {import("./utils/model-primary-key.js").ModelPrimaryKeyValue} - Primary key value.
|
|
1116
1139
|
*/
|
|
1117
1140
|
frontendModelPrimaryKeyValue(model) {
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1141
|
+
const primaryKey = this.frontendModelPrimaryKey()
|
|
1142
|
+
|
|
1143
|
+
return readModelPrimaryKeyValue(primaryKey, (attributeName) => model.readAttribute(attributeName))
|
|
1144
|
+
}
|
|
1122
1145
|
|
|
1123
|
-
|
|
1146
|
+
/**
|
|
1147
|
+
* Returns the authorized identities from a candidate cohort without per-record queries.
|
|
1148
|
+
* @param {object} args - Arguments.
|
|
1149
|
+
* @param {import("./utils/model-primary-key.js").ModelPrimaryKeyValue[]} args.identities - Candidate identities.
|
|
1150
|
+
* @param {typeof import("./database/record/index.js").default} args.modelClass - Model class owning the identity attributes.
|
|
1151
|
+
* @param {import("./utils/model-primary-key.js").ModelPrimaryKeyDefinition} args.primaryKey - Identity definition.
|
|
1152
|
+
* @param {import("./database/query/model-class-query.js").default<typeof import("./database/record/index.js").default>} args.query - Authorized query.
|
|
1153
|
+
* @returns {Promise<Set<string>>} - Canonical authorized identity keys.
|
|
1154
|
+
*/
|
|
1155
|
+
async frontendModelAuthorizedIdentitySet({identities, modelClass, primaryKey, query}) {
|
|
1156
|
+
if (!Array.isArray(primaryKey)) {
|
|
1157
|
+
const authorizedIds = await query
|
|
1158
|
+
.where({[primaryKey]: identities})
|
|
1159
|
+
.pluck(primaryKey)
|
|
1160
|
+
|
|
1161
|
+
return new Set(authorizedIds.map((value) => modelPrimaryKeyCacheKey(primaryKey, value)))
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
const maxCompositeCohortSize = Math.max(1, Math.floor(query.driver.maxInClauseValues() / primaryKey.length))
|
|
1165
|
+
const cohorts = query.driver.chunkValues(identities, (cohort) => query
|
|
1166
|
+
.clone()
|
|
1167
|
+
.where(compositeModelPrimaryKeyCohortSql({modelClass, primaryKey, query, values: cohort}))
|
|
1168
|
+
.toSql(), {maxCount: maxCompositeCohortSize})
|
|
1169
|
+
const authorizedIdentityKeys = new Set()
|
|
1170
|
+
|
|
1171
|
+
for (const cohort of cohorts) {
|
|
1172
|
+
const authorizedModels = await query
|
|
1173
|
+
.clone()
|
|
1174
|
+
.where(compositeModelPrimaryKeyCohortSql({modelClass, primaryKey, query, values: cohort}))
|
|
1175
|
+
.toArray()
|
|
1176
|
+
|
|
1177
|
+
for (const model of authorizedModels) {
|
|
1178
|
+
const identity = readModelPrimaryKeyValue(primaryKey, (attributeName) => model.readAttribute(attributeName))
|
|
1179
|
+
|
|
1180
|
+
authorizedIdentityKeys.add(modelPrimaryKeyCacheKey(primaryKey, identity))
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
return authorizedIdentityKeys
|
|
1124
1185
|
}
|
|
1125
1186
|
|
|
1126
1187
|
/**
|
|
@@ -1134,14 +1195,16 @@ export default class FrontendModelController extends Controller {
|
|
|
1134
1195
|
if (models.length === 0) return models
|
|
1135
1196
|
|
|
1136
1197
|
const primaryKey = this.frontendModelPrimaryKey()
|
|
1137
|
-
const ids = models.map((model) => this.frontendModelPrimaryKeyValue(model))
|
|
1138
|
-
const authorizedQuery = this.frontendModelAuthorizedQuery(action).where({[primaryKey]: ids})
|
|
1139
|
-
|
|
1140
|
-
const authorizedIdsRaw = await authorizedQuery.pluck(primaryKey)
|
|
1141
1198
|
|
|
1142
|
-
const
|
|
1199
|
+
const identities = models.map((model) => this.frontendModelPrimaryKeyValue(model))
|
|
1200
|
+
const authorizedIds = await this.frontendModelAuthorizedIdentitySet({
|
|
1201
|
+
identities,
|
|
1202
|
+
modelClass: this.frontendModelClass(),
|
|
1203
|
+
primaryKey,
|
|
1204
|
+
query: this.frontendModelAuthorizedQuery(action)
|
|
1205
|
+
})
|
|
1143
1206
|
|
|
1144
|
-
return models.filter((model) => authorizedIds.has(this.frontendModelPrimaryKeyValue(model)))
|
|
1207
|
+
return models.filter((model) => authorizedIds.has(modelPrimaryKeyCacheKey(primaryKey, this.frontendModelPrimaryKeyValue(model))))
|
|
1145
1208
|
}
|
|
1146
1209
|
|
|
1147
1210
|
/**
|
|
@@ -1158,7 +1221,7 @@ export default class FrontendModelController extends Controller {
|
|
|
1158
1221
|
/**
|
|
1159
1222
|
* Runs frontend model find record.
|
|
1160
1223
|
* @param {"find" | "update" | "destroy" | "attach" | "attachmentList" | "download" | "url"} action - Frontend action.
|
|
1161
|
-
* @param {
|
|
1224
|
+
* @param {import("./utils/model-primary-key.js").ModelPrimaryKeyValue} id - Record id.
|
|
1162
1225
|
* @returns {Promise<import("./database/record/index.js").default | null>} - Located model record.
|
|
1163
1226
|
*/
|
|
1164
1227
|
async frontendModelFindRecord(action, id) {
|
|
@@ -1471,17 +1534,21 @@ export default class FrontendModelController extends Controller {
|
|
|
1471
1534
|
if (candidates.length === 0) continue
|
|
1472
1535
|
|
|
1473
1536
|
const primaryKey = modelClass.primaryKey()
|
|
1474
|
-
const ids = candidates
|
|
1475
|
-
.map((record) => record.readAttribute(primaryKey))
|
|
1476
|
-
.filter((value) => value !== null && value !== undefined)
|
|
1477
|
-
if (ids.length === 0) continue
|
|
1478
1537
|
|
|
1479
1538
|
for (const action of entry.actions) {
|
|
1539
|
+
/** @type {Set<string>} */
|
|
1480
1540
|
let allowedIds
|
|
1481
1541
|
try {
|
|
1482
|
-
const authorizedQuery = modelClass.accessibleFor(action, ability)
|
|
1483
|
-
|
|
1484
|
-
|
|
1542
|
+
const authorizedQuery = modelClass.accessibleFor(action, ability)
|
|
1543
|
+
|
|
1544
|
+
const identities = candidates.map((record) => record.id())
|
|
1545
|
+
|
|
1546
|
+
allowedIds = await this.frontendModelAuthorizedIdentitySet({
|
|
1547
|
+
identities,
|
|
1548
|
+
modelClass,
|
|
1549
|
+
primaryKey,
|
|
1550
|
+
query: authorizedQuery
|
|
1551
|
+
})
|
|
1485
1552
|
} catch (error) {
|
|
1486
1553
|
// An ability with no allow rules for the action throws via
|
|
1487
1554
|
// `accessibleFor`; treat as a universal deny so the frontend
|
|
@@ -1492,8 +1559,8 @@ export default class FrontendModelController extends Controller {
|
|
|
1492
1559
|
}
|
|
1493
1560
|
|
|
1494
1561
|
for (const record of candidates) {
|
|
1495
|
-
const idValue = record.
|
|
1496
|
-
const allowed =
|
|
1562
|
+
const idValue = record.id()
|
|
1563
|
+
const allowed = allowedIds.has(modelPrimaryKeyCacheKey(primaryKey, idValue))
|
|
1497
1564
|
record._setComputedAbility(action, allowed)
|
|
1498
1565
|
}
|
|
1499
1566
|
}
|
|
@@ -1659,7 +1726,7 @@ export default class FrontendModelController extends Controller {
|
|
|
1659
1726
|
*/
|
|
1660
1727
|
frontendModelMssqlDistinctByPrimaryKeyQuery({query}) {
|
|
1661
1728
|
const modelClass = this.frontendModelClass()
|
|
1662
|
-
const primaryKey = modelClass.primaryKey()
|
|
1729
|
+
const primaryKey = scalarModelPrimaryKey(modelClass.primaryKey(), "MSSQL distinct frontend-model queries")
|
|
1663
1730
|
const rootTableSql = query.driver.quoteTable(modelClass.tableName())
|
|
1664
1731
|
const primaryKeySql = `${rootTableSql}.${query.driver.quoteColumn(primaryKey)}`
|
|
1665
1732
|
const distinctIdsQuery = query.clone()
|
|
@@ -2972,7 +3039,7 @@ export default class FrontendModelController extends Controller {
|
|
|
2972
3039
|
const authorizedIdsByClass = new Map()
|
|
2973
3040
|
/**
|
|
2974
3041
|
* Primary keys by class.
|
|
2975
|
-
* @type {Map<typeof import("./database/record/index.js").default,
|
|
3042
|
+
* @type {Map<typeof import("./database/record/index.js").default, import("./utils/model-primary-key.js").ModelPrimaryKeyDefinition>} */
|
|
2976
3043
|
const primaryKeysByClass = new Map()
|
|
2977
3044
|
|
|
2978
3045
|
for (const [relatedModelClass, relatedModels] of modelsByClass.entries()) {
|
|
@@ -2993,22 +3060,16 @@ export default class FrontendModelController extends Controller {
|
|
|
2993
3060
|
}
|
|
2994
3061
|
|
|
2995
3062
|
const primaryKey = relatedModelClass.primaryKey()
|
|
2996
|
-
const
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
}
|
|
3004
|
-
|
|
3005
|
-
const authorizedIdsRaw = await relatedModelClass
|
|
3006
|
-
.accessibleFor(abilityAction)
|
|
3007
|
-
.where({[primaryKey]: ids})
|
|
3008
|
-
.pluck(primaryKey)
|
|
3063
|
+
const identities = relatedModels.map((model) => model.id())
|
|
3064
|
+
const authorizedIds = await this.frontendModelAuthorizedIdentitySet({
|
|
3065
|
+
identities,
|
|
3066
|
+
modelClass: relatedModelClass,
|
|
3067
|
+
primaryKey,
|
|
3068
|
+
query: relatedModelClass.accessibleFor(abilityAction)
|
|
3069
|
+
})
|
|
3009
3070
|
|
|
3010
3071
|
primaryKeysByClass.set(relatedModelClass, primaryKey)
|
|
3011
|
-
authorizedIdsByClass.set(relatedModelClass,
|
|
3072
|
+
authorizedIdsByClass.set(relatedModelClass, authorizedIds)
|
|
3012
3073
|
}
|
|
3013
3074
|
|
|
3014
3075
|
return models.filter((model) => {
|
|
@@ -3018,11 +3079,7 @@ export default class FrontendModelController extends Controller {
|
|
|
3018
3079
|
|
|
3019
3080
|
if (!authorizedIds || !primaryKey) return false
|
|
3020
3081
|
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
if (primaryKeyValue === undefined || primaryKeyValue === null) return false
|
|
3024
|
-
|
|
3025
|
-
return authorizedIds.has(String(primaryKeyValue))
|
|
3082
|
+
return authorizedIds.has(modelPrimaryKeyCacheKey(primaryKey, model.id()))
|
|
3026
3083
|
})
|
|
3027
3084
|
}
|
|
3028
3085
|
|
|
@@ -3141,8 +3198,10 @@ export default class FrontendModelController extends Controller {
|
|
|
3141
3198
|
const hasQueryData = Object.keys(queryDataValues).length > 0
|
|
3142
3199
|
const hasAbilities = Object.keys(computedAbilities).length > 0
|
|
3143
3200
|
const hasPreloaded = Object.keys(preloadedRelationships).length > 0
|
|
3201
|
+
const resource = this._serializationResourceInstanceForModel(model)
|
|
3202
|
+
const hasAttachmentOwner = Object.keys(resource?.resourceConfiguration().attachments || {}).length > 0
|
|
3144
3203
|
|
|
3145
|
-
if (!hasPreloaded && !hasCounts && !hasQueryData && !hasAbilities) {
|
|
3204
|
+
if (!hasPreloaded && !hasCounts && !hasQueryData && !hasAbilities && !hasAttachmentOwner) {
|
|
3146
3205
|
serializedModels.push(serializedAttributes)
|
|
3147
3206
|
continue
|
|
3148
3207
|
}
|
|
@@ -3156,6 +3215,17 @@ export default class FrontendModelController extends Controller {
|
|
|
3156
3215
|
if (hasCounts) serialized.__associationCounts = associationCounts
|
|
3157
3216
|
if (hasQueryData) serialized.__queryData = queryDataValues
|
|
3158
3217
|
if (hasAbilities) serialized.__abilities = computedAbilities
|
|
3218
|
+
if (hasAttachmentOwner) {
|
|
3219
|
+
if (!resource) throw new Error(`Missing frontend model resource for attachment owner ${model.getModelClass().getModelName()}`)
|
|
3220
|
+
|
|
3221
|
+
const modelClass = model.getModelClass()
|
|
3222
|
+
|
|
3223
|
+
serialized[ATTACHMENT_OWNER_KEY] = {
|
|
3224
|
+
recordId: modelPrimaryKeyCacheKey(modelClass.primaryKey(), model.id()),
|
|
3225
|
+
recordType: modelClass.getModelName(),
|
|
3226
|
+
resourceName: resource.modelName()
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3159
3229
|
|
|
3160
3230
|
serializedModels.push(serialized)
|
|
3161
3231
|
}
|
|
@@ -3465,7 +3535,8 @@ export default class FrontendModelController extends Controller {
|
|
|
3465
3535
|
|
|
3466
3536
|
const params = this.frontendModelParams()
|
|
3467
3537
|
const modelClass = this.frontendModelClass()
|
|
3468
|
-
const
|
|
3538
|
+
const primaryKey = this.frontendModelPrimaryKey()
|
|
3539
|
+
let id = params.id
|
|
3469
3540
|
|
|
3470
3541
|
if (action === "create") {
|
|
3471
3542
|
const mutationAttributes = frontendModelMutationAttributes(params)
|
|
@@ -3486,10 +3557,22 @@ export default class FrontendModelController extends Controller {
|
|
|
3486
3557
|
return frontendModelSerializedModelSuccess(serializedModel)
|
|
3487
3558
|
}
|
|
3488
3559
|
|
|
3489
|
-
if ((typeof id !== "string" && typeof id !== "number") || `${id}`.length < 1) {
|
|
3560
|
+
if (!Array.isArray(primaryKey) && ((typeof id !== "string" && typeof id !== "number") || `${id}`.length < 1)) {
|
|
3490
3561
|
return this.frontendModelErrorPayload("Expected model id.", {errorType: "validation_error"})
|
|
3491
3562
|
}
|
|
3492
3563
|
|
|
3564
|
+
try {
|
|
3565
|
+
if (Array.isArray(primaryKey) && typeof id === "string") {
|
|
3566
|
+
id = modelPrimaryKeyValueFromCacheKey(primaryKey, id)
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
modelPrimaryKeyConditions(primaryKey, id)
|
|
3570
|
+
} catch (error) {
|
|
3571
|
+
if (!(error instanceof TypeError)) throw error
|
|
3572
|
+
|
|
3573
|
+
return this.frontendModelErrorPayload(error.message, {errorType: "validation_error"})
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3493
3576
|
if (action === "attach") {
|
|
3494
3577
|
const attachmentName = params.attachmentName
|
|
3495
3578
|
const attachmentInput = params.attachment
|