json-rest-api 1.0.14 → 1.0.15
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/connectors_ideas/fastify.js +66 -68
- package/connectors_ideas/koa-plugin.js +50 -51
- package/eslint.config.js +11 -2
- package/examples/url-override-example.js +63 -63
- package/index.js +13 -13
- package/lib/enhanced-logger.js +51 -51
- package/lib/error-formatter.js +61 -61
- package/lib/rest-api-errors.js +28 -28
- package/package.json +1 -1
- package/plugins/core/bulk-operations-plugin.js +142 -145
- package/plugins/core/connectors/express-plugin.js +210 -217
- package/plugins/core/connectors/lib/busboy-detector.js +84 -84
- package/plugins/core/connectors/lib/formidable-detector.js +50 -50
- package/plugins/core/connectors/lib/request-helpers.js +74 -74
- package/plugins/core/file-handling-plugin.js +125 -125
- package/plugins/core/lib/anyapi/anyapi-registry.js +254 -254
- package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +287 -287
- package/plugins/core/lib/anyapi/schema-utils.js +101 -101
- package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +41 -41
- package/plugins/core/lib/dbTablesOperations.js +134 -135
- package/plugins/core/lib/fractional-positioning.js +74 -76
- package/plugins/core/lib/querying/knex-cross-table-search.js +320 -320
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +144 -144
- package/plugins/core/lib/querying/knex-pagination-helpers.js +164 -164
- package/plugins/core/lib/querying/knex-process-includes.js +50 -50
- package/plugins/core/lib/querying/knex-query-helpers-base.js +31 -31
- package/plugins/core/lib/querying/knex-query-helpers.js +451 -452
- package/plugins/core/lib/querying/knex-relationship-includes.js +1160 -1170
- package/plugins/core/lib/querying/knex-window-queries.js +89 -89
- package/plugins/core/lib/querying/url-helpers.js +35 -35
- package/plugins/core/lib/querying-writing/compile-schemas.js +97 -98
- package/plugins/core/lib/querying-writing/connectors-query-parser.js +31 -31
- package/plugins/core/lib/querying-writing/database-capabilities.js +94 -94
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +76 -76
- package/plugins/core/lib/querying-writing/default-data-helpers.js +27 -27
- package/plugins/core/lib/querying-writing/field-utils.js +44 -42
- package/plugins/core/lib/querying-writing/knex-constants.js +15 -16
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +117 -115
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +41 -41
- package/plugins/core/lib/querying-writing/payload-validators.js +275 -275
- package/plugins/core/lib/querying-writing/schema-helpers.js +129 -129
- package/plugins/core/lib/querying-writing/scope-validations.js +143 -146
- package/plugins/core/lib/querying-writing/simplified-helpers.js +133 -129
- package/plugins/core/lib/storage/storage-adapter.js +68 -68
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +31 -31
- package/plugins/core/lib/writing/many-to-many-manipulations.js +81 -81
- package/plugins/core/lib/writing/relationship-processor.js +58 -58
- package/plugins/core/rest-api-access.js +8 -8
- package/plugins/core/rest-api-anyapi-knex-plugin.js +920 -920
- package/plugins/core/rest-api-cors-plugin.js +170 -171
- package/plugins/core/rest-api-knex-plugin.js +482 -493
- package/plugins/core/rest-api-multihome-plugin.js +247 -251
- package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +5 -5
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +20 -21
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +44 -44
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +23 -23
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +15 -15
- package/plugins/core/rest-api-plugin-methods/add-route.js +9 -9
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +5 -6
- package/plugins/core/rest-api-plugin-methods/common.js +295 -297
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +35 -37
- package/plugins/core/rest-api-plugin-methods/delete.js +57 -59
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +172 -171
- package/plugins/core/rest-api-plugin-methods/get-related.js +96 -96
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +16 -18
- package/plugins/core/rest-api-plugin-methods/get.js +86 -86
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +16 -17
- package/plugins/core/rest-api-plugin-methods/patch.js +63 -64
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +54 -55
- package/plugins/core/rest-api-plugin-methods/post.js +61 -62
- package/plugins/core/rest-api-plugin-methods/put.js +86 -87
- package/plugins/core/rest-api-plugin-methods/query.js +88 -88
- package/plugins/core/rest-api-plugin-methods/release.js +5 -6
- package/plugins/core/rest-api-plugin.js +76 -78
- package/plugins/core/rest-api-positioning-plugin.js +251 -252
- package/plugins/core/socketio-plugin.js +240 -236
- package/plugins/storage/local-storage.js +103 -103
- package/plugins/storage/s3-storage.js +41 -42
- package/quickTest.js +37 -37
- package/scripts/debug-anyapi-review-filter.mjs +18 -18
- package/scripts/inspect-anyapi-descriptor-country.mjs +10 -10
- package/scripts/inspect-anyapi-descriptor.mjs +10 -10
- package/scripts/run-docs.js +112 -113
- package/test-debug.js +28 -28
- package/tests/TEST_TEMPLATE.test.js +51 -51
- package/tests/anyapi-basic.test.js +42 -42
- package/tests/anyapi-cursor-pagination.test.js +48 -48
- package/tests/bulk-operations.test.js +208 -208
- package/tests/computed-fields.test.js +204 -204
- package/tests/cors.test.js +412 -413
- package/tests/curl.test.js +275 -276
- package/tests/custom-idproperty-relationships.test.js +266 -266
- package/tests/field-getters.test.js +132 -132
- package/tests/field-setters.test.js +110 -110
- package/tests/fixtures/api-configs.js +699 -699
- package/tests/helpers/storage-mode.js +32 -32
- package/tests/helpers/test-utils.js +76 -76
- package/tests/include-depth-validation.test.js +76 -76
- package/tests/includes.test.js +145 -145
- package/tests/multihome.test.js +175 -175
- package/tests/nested-includes.test.js +255 -255
- package/tests/pagination-cursor-multifield.test.js +164 -164
- package/tests/pagination-enhanced.test.js +225 -225
- package/tests/polymorphic-relationships.test.js +96 -96
- package/tests/polymorphic-simple.test.js +44 -44
- package/tests/positioning-advanced.test.js +226 -226
- package/tests/positioning-extended.test.js +399 -399
- package/tests/positioning-multifilter.test.js +204 -204
- package/tests/positioning.test.js +266 -266
- package/tests/queries.test.js +184 -184
- package/tests/query-limits.test.js +154 -155
- package/tests/relationship-endpoints.test.js +127 -127
- package/tests/relationships.test.js +178 -179
- package/tests/rest-api-access.test.js +86 -86
- package/tests/rest-api.test.js +103 -103
- package/tests/searchschema-merge.test.js +93 -93
- package/tests/socketio.test.js +326 -326
- package/tests/virtual-fields.test.js +164 -164
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const STRING_SLOT_COUNT = 10
|
|
2
|
-
const NUMBER_SLOT_COUNT = 10
|
|
3
|
-
const BOOLEAN_SLOT_COUNT = 5
|
|
4
|
-
const DATE_SLOT_COUNT = 5
|
|
5
|
-
const JSON_SLOT_COUNT = 5
|
|
6
|
-
const BELONGS_TO_SLOT_COUNT = 5
|
|
1
|
+
const STRING_SLOT_COUNT = 10
|
|
2
|
+
const NUMBER_SLOT_COUNT = 10
|
|
3
|
+
const BOOLEAN_SLOT_COUNT = 5
|
|
4
|
+
const DATE_SLOT_COUNT = 5
|
|
5
|
+
const JSON_SLOT_COUNT = 5
|
|
6
|
+
const BELONGS_TO_SLOT_COUNT = 5
|
|
7
7
|
|
|
8
8
|
export const SLOT_LIMITS = {
|
|
9
9
|
string: STRING_SLOT_COUNT,
|
|
@@ -12,7 +12,7 @@ export const SLOT_LIMITS = {
|
|
|
12
12
|
date: DATE_SLOT_COUNT,
|
|
13
13
|
json: JSON_SLOT_COUNT,
|
|
14
14
|
belongsTo: BELONGS_TO_SLOT_COUNT,
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
16
|
|
|
17
17
|
export const DEFAULT_CANONICAL_CONFIG = {
|
|
18
18
|
scopeName: 'anyapi_records',
|
|
@@ -25,137 +25,137 @@ export const DEFAULT_CANONICAL_CONFIG = {
|
|
|
25
25
|
leftResourceColumn: 'left_resource',
|
|
26
26
|
rightIdColumn: 'right_id',
|
|
27
27
|
rightResourceColumn: 'right_resource',
|
|
28
|
-
}
|
|
28
|
+
}
|
|
29
29
|
|
|
30
30
|
const ensureTable = async (knex, tableName, tableBuilder) => {
|
|
31
|
-
const exists = await knex.schema.hasTable(tableName)
|
|
31
|
+
const exists = await knex.schema.hasTable(tableName)
|
|
32
32
|
if (!exists) {
|
|
33
|
-
await knex.schema.createTable(tableName, tableBuilder)
|
|
33
|
+
await knex.schema.createTable(tableName, tableBuilder)
|
|
34
34
|
}
|
|
35
|
-
}
|
|
35
|
+
}
|
|
36
36
|
|
|
37
37
|
const ensureIndex = async (knex, tableName, columns, indexName) => {
|
|
38
|
-
const exists = await knex.schema.hasTable(tableName)
|
|
39
|
-
if (!exists) return
|
|
40
|
-
const hasIndex = await knex.schema.withSchema('main').hasIndex?.(tableName, indexName)
|
|
38
|
+
const exists = await knex.schema.hasTable(tableName)
|
|
39
|
+
if (!exists) return
|
|
40
|
+
const hasIndex = await knex.schema.withSchema('main').hasIndex?.(tableName, indexName)
|
|
41
41
|
if (!hasIndex) {
|
|
42
42
|
await knex.schema.table(tableName, (table) => {
|
|
43
|
-
table.index(columns, indexName)
|
|
44
|
-
})
|
|
43
|
+
table.index(columns, indexName)
|
|
44
|
+
})
|
|
45
45
|
}
|
|
46
|
-
}
|
|
46
|
+
}
|
|
47
47
|
|
|
48
48
|
export const ensureAnyApiSchema = async (knex) => {
|
|
49
49
|
await ensureTable(knex, 'any_records', (table) => {
|
|
50
|
-
table.increments('id').primary()
|
|
51
|
-
table.string('tenant_id').notNullable()
|
|
52
|
-
table.string('resource').notNullable()
|
|
50
|
+
table.increments('id').primary()
|
|
51
|
+
table.string('tenant_id').notNullable()
|
|
52
|
+
table.string('resource').notNullable()
|
|
53
53
|
|
|
54
54
|
for (let index = 1; index <= STRING_SLOT_COUNT; index += 1) {
|
|
55
|
-
table.text(`string_${index}`)
|
|
55
|
+
table.text(`string_${index}`)
|
|
56
56
|
}
|
|
57
57
|
for (let index = 1; index <= NUMBER_SLOT_COUNT; index += 1) {
|
|
58
|
-
table.double(`number_${index}`)
|
|
58
|
+
table.double(`number_${index}`)
|
|
59
59
|
}
|
|
60
60
|
for (let index = 1; index <= BOOLEAN_SLOT_COUNT; index += 1) {
|
|
61
|
-
table.boolean(`boolean_${index}`)
|
|
61
|
+
table.boolean(`boolean_${index}`)
|
|
62
62
|
}
|
|
63
63
|
for (let index = 1; index <= DATE_SLOT_COUNT; index += 1) {
|
|
64
|
-
table.dateTime(`date_${index}`)
|
|
64
|
+
table.dateTime(`date_${index}`)
|
|
65
65
|
}
|
|
66
66
|
for (let index = 1; index <= JSON_SLOT_COUNT; index += 1) {
|
|
67
|
-
table.text(`json_${index}`)
|
|
67
|
+
table.text(`json_${index}`)
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
for (let index = 1; index <= BELONGS_TO_SLOT_COUNT; index += 1) {
|
|
71
|
-
table.string(`rel_${index}_id`)
|
|
72
|
-
table.string(`rel_${index}_type`)
|
|
71
|
+
table.string(`rel_${index}_id`)
|
|
72
|
+
table.string(`rel_${index}_type`)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
76
|
-
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
77
|
-
table.dateTime('deleted_at')
|
|
75
|
+
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
76
|
+
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
77
|
+
table.dateTime('deleted_at')
|
|
78
78
|
|
|
79
|
-
table.index(['tenant_id', 'resource'])
|
|
80
|
-
table.index(['resource'])
|
|
81
|
-
})
|
|
79
|
+
table.index(['tenant_id', 'resource'])
|
|
80
|
+
table.index(['resource'])
|
|
81
|
+
})
|
|
82
82
|
|
|
83
83
|
await ensureTable(knex, 'any_links', (table) => {
|
|
84
|
-
table.increments('id').primary()
|
|
85
|
-
table.string('tenant_id').notNullable()
|
|
86
|
-
table.string('relationship').notNullable()
|
|
87
|
-
table.string('inverse_relationship')
|
|
88
|
-
table.string('left_resource').notNullable()
|
|
89
|
-
table.string('left_id').notNullable()
|
|
90
|
-
table.string('right_resource').notNullable()
|
|
91
|
-
table.string('right_id').notNullable()
|
|
92
|
-
table.text('payload')
|
|
93
|
-
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
94
|
-
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
95
|
-
|
|
96
|
-
table.index(['tenant_id', 'relationship'])
|
|
97
|
-
table.index(['tenant_id', 'inverse_relationship'])
|
|
98
|
-
table.index(['tenant_id', 'left_resource'])
|
|
99
|
-
table.index(['tenant_id', 'right_resource'])
|
|
100
|
-
table.index(['relationship'])
|
|
101
|
-
table.index(['inverse_relationship'])
|
|
102
|
-
})
|
|
84
|
+
table.increments('id').primary()
|
|
85
|
+
table.string('tenant_id').notNullable()
|
|
86
|
+
table.string('relationship').notNullable()
|
|
87
|
+
table.string('inverse_relationship')
|
|
88
|
+
table.string('left_resource').notNullable()
|
|
89
|
+
table.string('left_id').notNullable()
|
|
90
|
+
table.string('right_resource').notNullable()
|
|
91
|
+
table.string('right_id').notNullable()
|
|
92
|
+
table.text('payload')
|
|
93
|
+
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
94
|
+
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
95
|
+
|
|
96
|
+
table.index(['tenant_id', 'relationship'])
|
|
97
|
+
table.index(['tenant_id', 'inverse_relationship'])
|
|
98
|
+
table.index(['tenant_id', 'left_resource'])
|
|
99
|
+
table.index(['tenant_id', 'right_resource'])
|
|
100
|
+
table.index(['relationship'])
|
|
101
|
+
table.index(['inverse_relationship'])
|
|
102
|
+
})
|
|
103
103
|
|
|
104
104
|
await ensureTable(knex, 'any_resource_configs', (table) => {
|
|
105
|
-
table.increments('id').primary()
|
|
106
|
-
table.string('tenant_id').notNullable()
|
|
107
|
-
table.string('resource').notNullable()
|
|
108
|
-
table.text('schema_json').notNullable()
|
|
109
|
-
table.text('relationships_json').notNullable()
|
|
110
|
-
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
111
|
-
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
105
|
+
table.increments('id').primary()
|
|
106
|
+
table.string('tenant_id').notNullable()
|
|
107
|
+
table.string('resource').notNullable()
|
|
108
|
+
table.text('schema_json').notNullable()
|
|
109
|
+
table.text('relationships_json').notNullable()
|
|
110
|
+
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
111
|
+
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
112
112
|
|
|
113
|
-
table.unique(['tenant_id', 'resource'])
|
|
114
|
-
})
|
|
113
|
+
table.unique(['tenant_id', 'resource'])
|
|
114
|
+
})
|
|
115
115
|
|
|
116
116
|
await ensureTable(knex, 'any_field_configs', (table) => {
|
|
117
|
-
table.increments('id').primary()
|
|
117
|
+
table.increments('id').primary()
|
|
118
118
|
table.integer('resource_config_id').notNullable()
|
|
119
|
-
.references('id').inTable('any_resource_configs').onDelete('CASCADE')
|
|
120
|
-
table.string('field_name').notNullable()
|
|
121
|
-
table.string('slot_type').notNullable()
|
|
122
|
-
table.integer('slot_index').notNullable()
|
|
123
|
-
table.string('slot_column').notNullable()
|
|
124
|
-
table.boolean('nullable').defaultTo(false).notNullable()
|
|
125
|
-
table.boolean('required').defaultTo(false).notNullable()
|
|
126
|
-
table.string('target_resource')
|
|
127
|
-
table.string('alias')
|
|
128
|
-
table.text('meta_json')
|
|
129
|
-
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
130
|
-
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
131
|
-
|
|
132
|
-
table.unique(['resource_config_id', 'field_name'])
|
|
133
|
-
table.index(['resource_config_id'])
|
|
134
|
-
})
|
|
119
|
+
.references('id').inTable('any_resource_configs').onDelete('CASCADE')
|
|
120
|
+
table.string('field_name').notNullable()
|
|
121
|
+
table.string('slot_type').notNullable()
|
|
122
|
+
table.integer('slot_index').notNullable()
|
|
123
|
+
table.string('slot_column').notNullable()
|
|
124
|
+
table.boolean('nullable').defaultTo(false).notNullable()
|
|
125
|
+
table.boolean('required').defaultTo(false).notNullable()
|
|
126
|
+
table.string('target_resource')
|
|
127
|
+
table.string('alias')
|
|
128
|
+
table.text('meta_json')
|
|
129
|
+
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
130
|
+
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
131
|
+
|
|
132
|
+
table.unique(['resource_config_id', 'field_name'])
|
|
133
|
+
table.index(['resource_config_id'])
|
|
134
|
+
})
|
|
135
135
|
|
|
136
136
|
await ensureTable(knex, 'any_relationship_configs', (table) => {
|
|
137
|
-
table.increments('id').primary()
|
|
137
|
+
table.increments('id').primary()
|
|
138
138
|
table.integer('resource_config_id').notNullable()
|
|
139
|
-
.references('id').inTable('any_resource_configs').onDelete('CASCADE')
|
|
140
|
-
table.string('relationship_name').notNullable()
|
|
141
|
-
table.string('relationship_type').notNullable()
|
|
142
|
-
table.string('target_resource')
|
|
143
|
-
table.integer('slot_index')
|
|
144
|
-
table.string('id_column')
|
|
145
|
-
table.string('type_column')
|
|
146
|
-
table.string('relationship_key')
|
|
147
|
-
table.string('through')
|
|
148
|
-
table.string('foreign_key')
|
|
149
|
-
table.string('other_key')
|
|
150
|
-
table.string('alias')
|
|
151
|
-
table.text('meta_json')
|
|
152
|
-
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
153
|
-
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
154
|
-
|
|
155
|
-
table.unique(['resource_config_id', 'relationship_name'])
|
|
156
|
-
table.index(['resource_config_id'])
|
|
157
|
-
})
|
|
158
|
-
}
|
|
139
|
+
.references('id').inTable('any_resource_configs').onDelete('CASCADE')
|
|
140
|
+
table.string('relationship_name').notNullable()
|
|
141
|
+
table.string('relationship_type').notNullable()
|
|
142
|
+
table.string('target_resource')
|
|
143
|
+
table.integer('slot_index')
|
|
144
|
+
table.string('id_column')
|
|
145
|
+
table.string('type_column')
|
|
146
|
+
table.string('relationship_key')
|
|
147
|
+
table.string('through')
|
|
148
|
+
table.string('foreign_key')
|
|
149
|
+
table.string('other_key')
|
|
150
|
+
table.string('alias')
|
|
151
|
+
table.text('meta_json')
|
|
152
|
+
table.dateTime('created_at').defaultTo(knex.fn.now()).notNullable()
|
|
153
|
+
table.dateTime('updated_at').defaultTo(knex.fn.now()).notNullable()
|
|
154
|
+
|
|
155
|
+
table.unique(['resource_config_id', 'relationship_name'])
|
|
156
|
+
table.index(['resource_config_id'])
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
159
|
|
|
160
160
|
export const SLOT_POOLS = {
|
|
161
161
|
string: Array.from({ length: STRING_SLOT_COUNT }, (_, index) => `string_${index + 1}`),
|
|
@@ -164,7 +164,7 @@ export const SLOT_POOLS = {
|
|
|
164
164
|
date: Array.from({ length: DATE_SLOT_COUNT }, (_, index) => `date_${index + 1}`),
|
|
165
165
|
json: Array.from({ length: JSON_SLOT_COUNT }, (_, index) => `json_${index + 1}`),
|
|
166
166
|
belongsTo: Array.from({ length: BELONGS_TO_SLOT_COUNT }, (_, index) => index + 1),
|
|
167
|
-
}
|
|
167
|
+
}
|
|
168
168
|
|
|
169
169
|
export const TYPE_TO_POOL = new Map([
|
|
170
170
|
['string', 'string'],
|
|
@@ -183,4 +183,4 @@ export const TYPE_TO_POOL = new Map([
|
|
|
183
183
|
['time', 'date'],
|
|
184
184
|
['json', 'json'],
|
|
185
185
|
['object', 'json'],
|
|
186
|
-
])
|
|
186
|
+
])
|
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
import { RestApiValidationError } from '../../../../../lib/rest-api-errors.js'
|
|
1
|
+
import { RestApiValidationError } from '../../../../../lib/rest-api-errors.js'
|
|
2
2
|
|
|
3
|
-
export const normalizeId = (value) => (value === null || value === undefined ? null : String(value))
|
|
3
|
+
export const normalizeId = (value) => (value === null || value === undefined ? null : String(value))
|
|
4
4
|
|
|
5
5
|
export const findSchemaFieldByAlias = (descriptor, alias) => {
|
|
6
|
-
if (!descriptor?.schema) return null
|
|
6
|
+
if (!descriptor?.schema) return null
|
|
7
7
|
for (const [fieldName, definition] of Object.entries(descriptor.schema)) {
|
|
8
|
-
if (!definition) continue
|
|
8
|
+
if (!definition) continue
|
|
9
9
|
if (definition.as === alias) {
|
|
10
|
-
return { fieldName, definition }
|
|
10
|
+
return { fieldName, definition }
|
|
11
11
|
}
|
|
12
12
|
if (definition.belongsTo && !definition.as) {
|
|
13
|
-
const inferredAlias = fieldName.endsWith('_id') ? fieldName.slice(0, -3) : fieldName
|
|
13
|
+
const inferredAlias = fieldName.endsWith('_id') ? fieldName.slice(0, -3) : fieldName
|
|
14
14
|
if (inferredAlias === alias) {
|
|
15
|
-
return { fieldName, definition }
|
|
15
|
+
return { fieldName, definition }
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
return null
|
|
20
|
-
}
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
21
|
|
|
22
22
|
export const resolveFieldInfo = (descriptor, field) => {
|
|
23
|
-
if (!descriptor) return null
|
|
23
|
+
if (!descriptor) return null
|
|
24
24
|
if (field === 'id') {
|
|
25
|
-
return { column: 'id', definition: { type: 'id' } }
|
|
25
|
+
return { column: 'id', definition: { type: 'id' } }
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const directField = descriptor.fields?.[field]
|
|
28
|
+
const directField = descriptor.fields?.[field]
|
|
29
29
|
if (directField?.slot) {
|
|
30
30
|
return {
|
|
31
31
|
column: directField.slot,
|
|
32
32
|
definition: descriptor.schema?.[field] || null,
|
|
33
|
-
}
|
|
33
|
+
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const belongsToInfo = descriptor.belongsTo?.[field]
|
|
36
|
+
const belongsToInfo = descriptor.belongsTo?.[field]
|
|
37
37
|
if (belongsToInfo?.idColumn) {
|
|
38
|
-
const schemaField = findSchemaFieldByAlias(descriptor, field)
|
|
38
|
+
const schemaField = findSchemaFieldByAlias(descriptor, field)
|
|
39
39
|
return {
|
|
40
40
|
column: belongsToInfo.idColumn,
|
|
41
41
|
definition: schemaField?.definition || null,
|
|
42
42
|
isRelationship: true,
|
|
43
|
-
}
|
|
43
|
+
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const aliasField = findSchemaFieldByAlias(descriptor, field)
|
|
46
|
+
const aliasField = findSchemaFieldByAlias(descriptor, field)
|
|
47
47
|
if (aliasField) {
|
|
48
|
-
const fieldEntry = descriptor.fields?.[aliasField.fieldName]
|
|
48
|
+
const fieldEntry = descriptor.fields?.[aliasField.fieldName]
|
|
49
49
|
if (fieldEntry?.slot) {
|
|
50
50
|
return {
|
|
51
51
|
column: fieldEntry.slot,
|
|
52
52
|
definition: aliasField.definition,
|
|
53
|
-
}
|
|
53
|
+
}
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
return null
|
|
58
|
-
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
59
|
|
|
60
60
|
export const coerceValueForDefinition = (value, definition, { isRelationship } = {}) => {
|
|
61
|
-
if (value === null || value === undefined) return null
|
|
61
|
+
if (value === null || value === undefined) return null
|
|
62
62
|
|
|
63
63
|
if (isRelationship) {
|
|
64
|
-
return normalizeId(value)
|
|
64
|
+
return normalizeId(value)
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const type = definition?.type || definition?.dataType
|
|
67
|
+
const type = definition?.type || definition?.dataType
|
|
68
68
|
if (!type) {
|
|
69
|
-
return value
|
|
69
|
+
return value
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if (['number', 'integer', 'float', 'decimal'].includes(type)) {
|
|
73
|
-
const numeric = Number(value)
|
|
74
|
-
return Number.isNaN(numeric) ? value : numeric
|
|
73
|
+
const numeric = Number(value)
|
|
74
|
+
return Number.isNaN(numeric) ? value : numeric
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (type === 'boolean') {
|
|
78
|
-
if (typeof value === 'boolean') return value
|
|
78
|
+
if (typeof value === 'boolean') return value
|
|
79
79
|
if (typeof value === 'string') {
|
|
80
|
-
if (value.toLowerCase() === 'true') return true
|
|
81
|
-
if (value.toLowerCase() === 'false') return false
|
|
80
|
+
if (value.toLowerCase() === 'true') return true
|
|
81
|
+
if (value.toLowerCase() === 'false') return false
|
|
82
82
|
}
|
|
83
|
-
return Boolean(value)
|
|
83
|
+
return Boolean(value)
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
if (['string', 'text', 'uuid', 'email'].includes(type)) {
|
|
87
|
-
return String(value)
|
|
87
|
+
return String(value)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
return value
|
|
91
|
-
}
|
|
90
|
+
return value
|
|
91
|
+
}
|
|
92
92
|
|
|
93
93
|
export const normalizeFilterValues = (rawValue, definition, options) => {
|
|
94
94
|
if (Array.isArray(rawValue)) {
|
|
95
|
-
return rawValue.map((item) => coerceValueForDefinition(item, definition, options))
|
|
95
|
+
return rawValue.map((item) => coerceValueForDefinition(item, definition, options))
|
|
96
96
|
}
|
|
97
|
-
return [coerceValueForDefinition(rawValue, definition, options)]
|
|
98
|
-
}
|
|
97
|
+
return [coerceValueForDefinition(rawValue, definition, options)]
|
|
98
|
+
}
|
|
99
99
|
|
|
100
100
|
export const ensureFilterableField = (descriptor, field) => {
|
|
101
|
-
const fieldInfo = resolveFieldInfo(descriptor, field)
|
|
101
|
+
const fieldInfo = resolveFieldInfo(descriptor, field)
|
|
102
102
|
if (!fieldInfo?.column) {
|
|
103
103
|
throw new RestApiValidationError('Invalid filter field', {
|
|
104
104
|
fields: [`filters.${field}`],
|
|
@@ -107,7 +107,7 @@ export const ensureFilterableField = (descriptor, field) => {
|
|
|
107
107
|
rule: 'unknown_field',
|
|
108
108
|
message: `Filter on '${field}' is not supported in AnyAPI mode`,
|
|
109
109
|
}],
|
|
110
|
-
})
|
|
110
|
+
})
|
|
111
111
|
}
|
|
112
|
-
return fieldInfo
|
|
113
|
-
}
|
|
112
|
+
return fieldInfo
|
|
113
|
+
}
|