@opengis/gis 0.2.177 → 0.2.179

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/{CardIcon-CMRD1LLh.js → CardIcon-DXkwtXRm.js} +1 -1
  2. package/dist/{EntityTablePage-XNNCltAN.js → EntityTablePage-VVLGYH2c.js} +1 -1
  3. package/dist/{ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-BqUjKTDY.js → ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-Dm5Fyjh9.js} +1 -1
  4. package/dist/{MapSettings-P3h-vHn3.js → MapSettings-CqgwRydf.js} +1 -1
  5. package/dist/MapToolbarRight.vue_vue_type_script_setup_true_lang-k3Zo1Hqm.js +1230 -0
  6. package/dist/{RastersTablePage-DnhGZLiH.js → RastersTablePage-Br_0QBMH.js} +1 -1
  7. package/dist/cartocss-C9OJk3VI.js +1460 -0
  8. package/dist/{import-utils-BCATsbB5.js → import-utils-DV6Yls6G.js} +1 -1
  9. package/dist/{index-cUagVqgA.js → index-hX98i4hU.js} +11 -11
  10. package/dist/index.css +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.umd.cjs +29 -29
  13. package/dist/{raster-CP0tUp3j.js → raster-NZ1TCJvt.js} +225 -219
  14. package/dist/{register-Cr-uQcby.js → register-BcPTINMn.js} +3 -3
  15. package/dist/{service-DwJaKrOd.js → service-NBe9fmay.js} +603 -597
  16. package/dist/{vs-datatable-B9WoVk-b.js → vs-datatable-CyjZaDoO.js} +3 -2
  17. package/module/gis/card/gis.metadata.table/index.yml +22 -22
  18. package/module/gis/card/gis.rasters.table/index.yml +11 -11
  19. package/module/gis/card/gis.registers.table/index.yml +23 -23
  20. package/module/gis/card/gis.services.table/index.yml +25 -25
  21. package/module/gis/cls/bool.yes_no.json +12 -12
  22. package/module/gis/cls/encoding.json +14 -14
  23. package/module/gis/cls/geom_type.json +14 -14
  24. package/module/gis/cls/gis.column_type.json +34 -34
  25. package/module/gis/cls/gis.column_view_type.json +26 -26
  26. package/module/gis/cls/gis.filter_type.json +22 -22
  27. package/module/gis/cls/language.json +10 -10
  28. package/module/gis/cls/meta.service_type.json +42 -42
  29. package/module/gis/cls/ogc.service.json +21 -21
  30. package/module/gis/cls/source_type.json +10 -10
  31. package/module/gis/cls/standarts.json +6 -6
  32. package/module/gis/cls/topic_category.json +106 -106
  33. package/module/gis/cls/update_frequency.json +29 -29
  34. package/module/gis/cls/view.json +21 -21
  35. package/module/gis/form/feedback.answer.form.json +10 -10
  36. package/module/gis/form/gis.cartocss.form.json +64 -64
  37. package/module/gis/form/gis.group_list.form.json +17 -17
  38. package/module/gis/form/gis.maps.form.json +61 -61
  39. package/module/gis/form/gis.metadata.form.json +240 -240
  40. package/module/gis/form/gis.metadata_new.form.json +195 -195
  41. package/module/gis/form/gis.ogc_service.form.json +51 -51
  42. package/module/gis/form/gis.rasters.form.json +76 -76
  43. package/module/gis/form/gis.registers_column.form.json +84 -84
  44. package/module/gis/form/gis.registers_filter.form.json +65 -65
  45. package/module/gis/form/gis.services.form.json +111 -111
  46. package/module/gis/form/gis.services_attributes.form.json +75 -75
  47. package/module/gis/form/gis.services_filter.form.json +65 -65
  48. package/module/gis/menu.json +43 -43
  49. package/module/gis/select/admin.cls.name.sql +9 -9
  50. package/module/gis/select/layer_list.sql +7 -7
  51. package/module/gis/select/layer_list_text.sql +7 -7
  52. package/module/gis/select/pg.column_name.geometry.sql +12 -12
  53. package/module/gis/select/pg.columns.parent.sql +6 -6
  54. package/module/gis/select/pg.table_name.sql +17 -17
  55. package/module/gis/select/service_id.sql +1 -1
  56. package/module/gis/select/srid.sql +7 -7
  57. package/module/gis/table/gis.cartocss.table.json +74 -74
  58. package/module/gis/table/gis.group_list.table.json +58 -58
  59. package/module/gis/table/gis.maps.table.json +110 -110
  60. package/module/gis/table/gis.metadata.table.json +70 -70
  61. package/module/gis/table/gis.metadata_new.table.json +137 -137
  62. package/module/gis/table/gis.ogc_service.table.json +100 -100
  63. package/module/gis/table/gis.rasters.table.json +113 -113
  64. package/module/gis/table/gis.registers.table.json +143 -143
  65. package/module/gis/table/gis.services.table.json +120 -120
  66. package/module/gis/table/site.gis.registers.table.json +88 -88
  67. package/module/gis/table/site.gis.services.table.json +106 -106
  68. package/module/gis/templates/ISO19136_2017_gml_template.xml +330 -330
  69. package/module/gis/tokens.yml +5 -5
  70. package/module/permissions/form/permissions.users.form.json +151 -151
  71. package/module/permissions/table/gis.permissions.table.json +97 -97
  72. package/package.json +1 -1
  73. package/server/routes/registers/functions/getFilterSQLParametrized/formatValue.js +312 -0
  74. package/server/routes/registers/functions/getFilterSQLParametrized/getFilterQuery.js +126 -0
  75. package/server/routes/registers/functions/getFilterSQLParametrized/getRangeQuery.js +353 -0
  76. package/server/routes/registers/functions/getFilterSQLParametrized/index.js +81 -0
  77. package/server/routes/registers/functions/getTableColumnMeta.js +2 -8
  78. package/server/routes/registers/functions/gis.suggest.js +5 -14
  79. package/server/routes/registers/functions/handleRegistryRequest.js +13 -5
  80. package/server/routes/registers/registers.route.test.js +32 -3
  81. package/server/routes/registers/registers.schema.js +7 -2
  82. package/dist/HeaderActions.vue_vue_type_script_setup_true_lang-BXQ3hKwa.js +0 -1099
  83. package/dist/cartocss-BkrOeQg4.js +0 -1586
@@ -0,0 +1,353 @@
1
+ /* eslint-disable no-restricted-globals */
2
+ const dateTypeList = [
3
+ "date",
4
+ "timestamp",
5
+ "timestamp without time zone",
6
+ "timestamp with time zone",
7
+ ];
8
+ const numberTypeList = [
9
+ "float8",
10
+ "int4",
11
+ "int8",
12
+ "numeric",
13
+ "double precision",
14
+ "integer",
15
+ "bigint",
16
+ ];
17
+
18
+ const isValidDate = (dateStr) => {
19
+ if (!dateStr) {
20
+ return false;
21
+ }
22
+
23
+ if (["min", "max", "cd", "cw", "cm", "cq", "cy"].includes(dateStr)) {
24
+ return true;
25
+ }
26
+
27
+ // iso date: 2024-01-01
28
+ if (dateStr.includes("-")) {
29
+ return new Date(dateStr).toString() !== "Invalid Date";
30
+ }
31
+
32
+ // locale date: 01.01.2024
33
+ const [dd, mm, yyyy] = dateStr.split(".");
34
+ return new Date(`${yyyy}-${mm}-${dd}`).toString() !== "Invalid Date";
35
+ };
36
+
37
+ const isValidNumber = (numStr) =>
38
+ ["min", "max"].includes(numStr) ? true : !isNaN(numStr);
39
+
40
+ function dt(y, m, d) {
41
+ return new Date(Date.UTC(y, m, d)).toISOString().slice(0, 10);
42
+ }
43
+
44
+ const dp = {
45
+ d: new Date().getDate(),
46
+ w: new Date().getDate() - (new Date().getDay() || 7) + 1,
47
+ m: new Date().getMonth(),
48
+ q: parseFloat((new Date().getMonth() / 4).toFixed()) * 3,
49
+ y: new Date().getFullYear(),
50
+ };
51
+ function formatDateISOString(date) {
52
+ if (!date?.includes(".")) return date;
53
+ const [day, month, year] = date.split(".");
54
+ return `${year}-${month}-${day}`;
55
+ }
56
+
57
+ function checkValid(
58
+ value,
59
+ fieldType,
60
+ filterType,
61
+ sql,
62
+ ) {
63
+ const sep =
64
+ (value.includes(",") ? "," : null) ||
65
+ (value.includes("_") ? "_" : null) ||
66
+ (value.includes("-") ? "-" : null);
67
+
68
+ // number range w/o valid separator => skip invalid filter
69
+ if (filterType === "range" && !sep) {
70
+ return { isvalid: false };
71
+ }
72
+
73
+ // date range, specific options: current day, week, month, year etc.
74
+ if (["date", "datepicker"].includes(filterType) && value === "cd") {
75
+ return {
76
+ min: dt(dp.y, dp.m, dp.d),
77
+ max: dt(dp.y, dp.m, dp.d),
78
+ isvalid: true,
79
+ };
80
+ }
81
+
82
+ if (["date", "datepicker"].includes(filterType) && value === "cw") {
83
+ return {
84
+ min: dt(dp.y, dp.m, dp.w),
85
+ max: dt(dp.y, dp.m, dp.w + 6),
86
+ isvalid: true,
87
+ };
88
+ }
89
+
90
+ if (["date", "datepicker"].includes(filterType) && value === "cm") {
91
+ return {
92
+ min: dt(dp.y, dp.m, 1),
93
+ max: dt(dp.y, dp.m + 1, 0),
94
+ isvalid: true,
95
+ };
96
+ }
97
+
98
+ if (["date", "datepicker"].includes(filterType) && value === "cq") {
99
+ return {
100
+ min: dt(dp.y, dp.q, 1),
101
+ max: dt(dp.y, dp.q + 3, 0),
102
+ isvalid: true,
103
+ };
104
+ }
105
+
106
+ if (["date", "datepicker"].includes(filterType) && value === "cy") {
107
+ return {
108
+ min: dt(dp.y, 0, 1),
109
+ max: dt(dp.y, 11, 31),
110
+ isvalid: true,
111
+ };
112
+ }
113
+
114
+ // specific period from now - days, months, years
115
+ if (
116
+ ["date", "datepicker"].includes(filterType) &&
117
+ value.match(/^(?<val>[1-9]\d*)(?<unit>d|m|y)$/)?.groups?.unit
118
+ ) {
119
+ const { val, unit } =
120
+ value.match(/^(?<val>[1-9]\d*)(?<unit>d|m|y)$/)?.groups || {};
121
+ const min =
122
+ (unit === "d" ? dt(dp.y, dp.m, dp.d - +val) : null) ||
123
+ (unit === "m" ? dt(dp.y, dp.m - +val, dp.d) : null) ||
124
+ (unit === "y" ? dt(dp.y - +val, dp.m, dp.d) : null);
125
+ return {
126
+ min,
127
+ max: dt(dp.y, dp.m, dp.d),
128
+ isvalid: isValidDate(min),
129
+ };
130
+ }
131
+
132
+ // specific quarter - before skip date value validation
133
+ if (
134
+ ["date", "datepicker"].includes(filterType) &&
135
+ value.match(/^(?<year>\d{4})-q(?<quarter>[1-4])$/)?.groups?.quarter
136
+ ) {
137
+ const { year, quarter } =
138
+ value.match(/^(?<year>\d{4})-q(?<quarter>[1-4])$/)?.groups || {};
139
+ const startMonth = +quarter * 3;
140
+ return {
141
+ min: dt(year, startMonth - 3, 1),
142
+ max: dt(year, startMonth, 0),
143
+ isvalid: true,
144
+ };
145
+ }
146
+
147
+ // date value validation
148
+ if (
149
+ ["date", "datepicker"].includes(filterType) &&
150
+ !sep &&
151
+ !isValidDate(value)
152
+ ) {
153
+ return { isvalid: false };
154
+ }
155
+
156
+ // specific date / day
157
+ if (
158
+ ["date", "datepicker"].includes(filterType) &&
159
+ value.match(/^\d{4}-\d{2}-\d{2}$/)?.[0]
160
+ ) {
161
+ return {
162
+ min: new Date(value).toISOString().split("T")[0],
163
+ max: new Date(value).toISOString().split("T")[0],
164
+ isvalid: true,
165
+ };
166
+ }
167
+
168
+ // specific month
169
+ if (
170
+ ["date", "datepicker"].includes(filterType) &&
171
+ value.match(/^\d{4}-\d{2}$/)?.[0]
172
+ ) {
173
+ return {
174
+ min: new Date(value).toISOString().split("T")[0],
175
+ max: dt(new Date(value).getFullYear(), new Date(value).getMonth() + 1, 0),
176
+ isvalid: true,
177
+ };
178
+ }
179
+
180
+ // specific year
181
+ if (
182
+ ["date", "datepicker"].includes(filterType) &&
183
+ value.match(/^\d{4}$/)?.[0]
184
+ ) {
185
+ return {
186
+ min: dt(value, 0, 1),
187
+ max: dt(value, 11, 31),
188
+ isvalid: true,
189
+ };
190
+ }
191
+
192
+ if (sep) {
193
+ const [minValue = "min", maxValue = "max"] = value.split(sep);
194
+ // range for numbers
195
+ if (filterType === "range" && fieldType) {
196
+ const isvalid =
197
+ (numberTypeList.includes(fieldType) || sql) &&
198
+ isValidNumber(minValue) &&
199
+ isValidNumber(maxValue);
200
+ return { min: minValue, max: maxValue, isvalid };
201
+ }
202
+
203
+ // date range, example: 01.01.2024-31.12.2024
204
+ if (["date", "datepicker"].includes(filterType)) {
205
+ const min = minValue === "min" ? minValue : formatDateISOString(minValue);
206
+ const max = maxValue === "max" ? maxValue : formatDateISOString(maxValue);
207
+ const isvalid =
208
+ fieldType &&
209
+ (dateTypeList.includes(fieldType) || sql) &&
210
+ isValidDate(min) &&
211
+ isValidDate(max);
212
+ return { min, max, isvalid };
213
+ }
214
+ }
215
+
216
+ return { isvalid: false };
217
+ }
218
+ export default function getRangeQuery({
219
+ value,
220
+ name,
221
+ fieldType,
222
+ filterType,
223
+ sql,
224
+ args: argsParams,
225
+ }) {
226
+ const { min, max, isvalid } = checkValid(
227
+ value,
228
+ fieldType,
229
+ filterType,
230
+ sql,
231
+ );
232
+ if (!isvalid) {
233
+ return { query: "false" };
234
+ }
235
+
236
+ const args = Array.from(argsParams || []);
237
+
238
+ const checkBoth =
239
+ sql && typeof sql === "string" && sql.includes("$1") && sql.includes("$2");
240
+
241
+ // with sql subquery
242
+ if (
243
+ filterType &&
244
+ ["date", "datepicker"].includes(filterType) &&
245
+ typeof sql === "string"
246
+ ) {
247
+ if (checkBoth) {
248
+ args.push(min);
249
+ args.push(max);
250
+ } else {
251
+ args.push(min || max);
252
+ }
253
+
254
+ return {
255
+ query: sql,
256
+ values: checkBoth ? [min, max] : [min || max],
257
+ };
258
+ }
259
+ // with sql subquery
260
+ if (filterType && ["range"].includes(filterType) && sql) {
261
+ if (checkBoth) {
262
+ args.push(min);
263
+ args.push(max);
264
+ } else {
265
+ args.push(min || max);
266
+ }
267
+
268
+ return {
269
+ query: sql,
270
+ values: checkBoth ? [min, max] : [min || max],
271
+ };
272
+ }
273
+
274
+ // preset date values
275
+ if (
276
+ filterType &&
277
+ ["date", "datepicker"].includes(filterType) &&
278
+ ["cd", "cw", "cm", "cq", "cy"].includes(value)
279
+ ) {
280
+ if (value === "cd") {
281
+ args.push(min);
282
+ } else {
283
+ args.push(min);
284
+ args.push(max);
285
+ }
286
+
287
+ const query =
288
+ value === "cd"
289
+ ? `${name}::date = $${args.length}::date`
290
+ : `${name}::date between $${args.length - 1}::date and $${args.length
291
+ }::date`;
292
+ return { query, values: value === "cd" ? [min] : [min, max] };
293
+ }
294
+
295
+ // same date
296
+ if (
297
+ filterType &&
298
+ ["date", "datepicker"].includes(filterType) &&
299
+ max &&
300
+ min &&
301
+ min === max
302
+ ) {
303
+ args.push(min);
304
+ return { query: `${name}::date = $${args.length}::date`, values: [min] };
305
+ }
306
+
307
+ // skip, same as 1=1
308
+ if (
309
+ filterType &&
310
+ ["range"].includes(filterType) &&
311
+ min === "min" &&
312
+ max === "max"
313
+ ) {
314
+ return { query: "true" };
315
+ }
316
+
317
+ // default
318
+ if (filterType && max === "max") {
319
+ args.push(min);
320
+ const query = ["date", "datepicker"].includes(filterType)
321
+ ? `${name} >= $${args.length}::date`
322
+ : `${name} >= $${args.length}`;
323
+ return {
324
+ query,
325
+ values: [min],
326
+ };
327
+ }
328
+
329
+ if (filterType && min === "min") {
330
+ args.push(max);
331
+ const query = ["date", "datepicker"].includes(filterType)
332
+ ? `${name} <= $${args.length}::date`
333
+ : `${name} <= $${args.length}`;
334
+ return { query, values: [max] };
335
+ }
336
+
337
+ if (filterType && ["date", "datepicker"].includes(filterType)) {
338
+ args.push(min);
339
+ args.push(max);
340
+ return {
341
+ query: `${name}::date between $${args.length - 1}::date and $${args.length
342
+ }::date`,
343
+ values: [min, max],
344
+ };
345
+ }
346
+
347
+ args.push(min);
348
+ args.push(max);
349
+ return {
350
+ query: `${name} between $${args.length - 1} and $${args.length}`,
351
+ values: [min, max],
352
+ };
353
+ }
@@ -0,0 +1,81 @@
1
+ import { BadRequestError } from "@opengis/fastify-table/errors.js";
2
+ import { pgClients } from "@opengis/fastify-table/utils.js";
3
+
4
+ // filter util
5
+ import getFilterQuery from "./getFilterQuery.js";
6
+
7
+ export default function getFilterSQLParametrized({
8
+ table,
9
+ fields = [],
10
+ filter,
11
+ search,
12
+ searchColumn: searchColumn1,
13
+ filterList,
14
+ query,
15
+ uid,
16
+ }, pg = pgClients.client) {
17
+ if (!pg) {
18
+ throw new Error("empty pg");
19
+ }
20
+
21
+ if (!table) {
22
+ throw BadRequestError("param table is required");
23
+ }
24
+
25
+ if (!pg.pk[table]) {
26
+ throw BadRequestError("param table is invalid");
27
+ }
28
+
29
+ const args = Array.from([]);
30
+
31
+ const autoSearchColumn = fields
32
+ ?.filter((el) => pg.pgType?.[el.dataTypeID] === "text")
33
+ ?.map((el) => `"${el.name}"`)
34
+ .join(",");
35
+
36
+ const searchColumn =
37
+ searchColumn1 ||
38
+ autoSearchColumn;
39
+
40
+ const where = [];
41
+
42
+
43
+ if (search && searchColumn && typeof search === "string") {
44
+ args.push(
45
+ `%${decodeURIComponent(search.replace(/%/g, "%25")).replace(
46
+ /%/g,
47
+ "\\%"
48
+ )}%`
49
+ );
50
+ where.push(
51
+ `(${searchColumn
52
+ .split(",")
53
+ ?.map((name) => `${name}::text ilike $${args.length}`)
54
+ ?.join(" or ")})`
55
+ );
56
+ }
57
+
58
+ const filters = getFilterQuery(
59
+ {
60
+ filter,
61
+ table,
62
+ fields,
63
+ filterList,
64
+ uid,
65
+ args,
66
+ },
67
+ pg
68
+ );
69
+
70
+ const filterQuery = filters
71
+ .filter((el) => el.query)
72
+ .map((el) => `${el.query}`)
73
+ .join(" and ");
74
+
75
+ const q = where
76
+ .concat([query, filterQuery])
77
+ .filter(Boolean)
78
+ .join(" and ");
79
+
80
+ return { q, args };
81
+ }
@@ -101,20 +101,14 @@ export default async function getTableColumnMeta(
101
101
  }
102
102
 
103
103
  const original = filtered
104
- ? `with c(id,text) as (select "${queryColumn.replace(
105
- /"/g,
106
- ""
107
- )}" as id, "${queryColumn.replace(
104
+ ? `with c(id,text) as (select rtrim( replace( replace( replace( encode("${queryColumn.replace(/"/g, '')}"::bytea, 'base64'), '+', '' ), '-', '' ), '/', '' ), '=' ) as id, "${queryColumn.replace(
108
105
  /"/g,
109
106
  ""
110
107
  )}" as text from ${tableName} t ${sqlTable} group by "${queryColumn.replace(
111
108
  /"/g,
112
109
  ""
113
110
  )}") select id, text from c`
114
- : `with c(id,text) as (select "${queryColumn.replace(
115
- /"/g,
116
- ""
117
- )}" as id, "${queryColumn.replace(
111
+ : `with c(id,text) as (select rtrim( replace( replace( replace( encode("${queryColumn.replace(/"/g, '')}"::bytea, 'base64'), '+', '' ), '-', '' ), '/', '' ), '=' ) as id, "${queryColumn.replace(
118
112
  /"/g,
119
113
  ""
120
114
  )}" as text, count(*) from ${tableName} t ${sqlTable} group by "${queryColumn.replace(
@@ -8,7 +8,7 @@ const rclient = getRedis();
8
8
  const count = false;
9
9
  const maxLimit = 100;
10
10
 
11
- export default async function gisSuggest({ token, key, val, limit: limit1 = 50 }, pg) {
11
+ export default async function gisSuggest({ token, key, val, uid, lang = 'ua', limit: limit1 = 50 }, pg) {
12
12
  const time = Date.now();
13
13
 
14
14
  const redisKey = `${pg?.options?.database}:registry-filter:${token}`;
@@ -163,10 +163,11 @@ export default async function gisSuggest({ token, key, val, limit: limit1 = 50 }
163
163
  };
164
164
  }
165
165
 
166
+ // table:column as select query
166
167
  const args = [];
167
168
  const where = [];
168
169
 
169
- // select, with search by text support
170
+ // search by text support
170
171
  if (key && typeof key === "string") {
171
172
  args.push(key.toLowerCase());
172
173
  where.push(searchQuery);
@@ -183,16 +184,6 @@ export default async function gisSuggest({ token, key, val, limit: limit1 = 50 }
183
184
  where.push(`${clsMeta.pk}=any($${args.length})`);
184
185
  }
185
186
 
186
- // filter
187
- if (pg.pk?.[registry.table_name] && !cls && dataTypeID) {
188
- where.push(
189
- `id in (select ${pg.pgType[dataTypeID]?.includes("[]")
190
- ? `unnest("${name}")`
191
- : `"${name}"`
192
- } from ${(registry.table_name).replace(/'/g, "''")})`
193
- );
194
- }
195
-
196
187
  // order by value if not specified explicitly
197
188
  const order = clsMeta.original.toLowerCase().includes("order by")
198
189
  ? ""
@@ -209,8 +200,8 @@ export default async function gisSuggest({ token, key, val, limit: limit1 = 50 }
209
200
  "''"
210
201
  )} o
211
202
  WHERE ${pg.pgType[dataTypeID]?.includes("[]")
212
- ? `c.id = ANY(o."${name}")`
213
- : `o."${name}" IS NOT DISTINCT FROM c.id`
203
+ ? `c.text = ANY(o."${name}")`
204
+ : `o."${name}" IS NOT DISTINCT FROM c.text`
214
205
  } ) and ${whereQuery || "true"}
215
206
  ${order} LIMIT $${args.length}::bigint`
216
207
  : `with c(id,text) as ( ${clsMeta.original} where ${whereQuery} ${order}) select * from c LIMIT $${args.length}::bigint`;
@@ -1,8 +1,9 @@
1
1
  import { createHash } from 'node:crypto';
2
2
 
3
- import { getMeta, getRedis, metaFormat, getFilterSQLParametrized } from "@opengis/fastify-table/utils.js";
3
+ import { getMeta, getRedis, metaFormat } from "@opengis/fastify-table/utils.js";
4
4
  import { populateFilterOptions } from './classifiers.js';
5
5
  import { extractVisibleColumns } from './columns.js';
6
+ import getFilterSQLParametrized from './getFilterSQLParametrized/index.js';
6
7
 
7
8
  const columnType = {
8
9
  text: 'text',
@@ -72,7 +73,10 @@ export async function handleRegistryRequest({
72
73
  WHERE ${pk} = $1
73
74
  `;
74
75
  const rows = await pg.query(sql, [object_id]).then(el => el.rows || []);
75
- if (!rows.length) throw new Error('Object not found');
76
+
77
+ if (!rows.length) {
78
+ throw new Error('Object not found');
79
+ }
76
80
 
77
81
  await metaFormat({
78
82
  rows, table: table_name, cls: classifiers, sufix: true,
@@ -95,13 +99,15 @@ export async function handleRegistryRequest({
95
99
  }
96
100
 
97
101
  const { search, filter } = query || {};
102
+
98
103
  const { q: sqlFilter, args = [] } = (filter || search)
99
- ? await getFilterSQLParametrized({
104
+ ? getFilterSQLParametrized({
100
105
  table: table_name,
106
+ fields: fields1,
101
107
  search,
102
108
  filter,
103
109
  filterList: activeFilters,
104
- })
110
+ }, pg)
105
111
  : { q: '1=1', args: [] };
106
112
 
107
113
  const whereConditions = [whereQuery, sqlFilter].filter(Boolean).join(' AND ');
@@ -113,7 +119,9 @@ export async function handleRegistryRequest({
113
119
  const sqlSelect = `SELECT "${pk}" as id ${selectColumns.length ? `, ${selectColumns.join(", ")}` : ''} ${geom ? `, st_asgeojson(${geom})::json as geom` : ''} ${sqlBase}`;
114
120
  const dataQuery = `${sqlSelect} ${sqlOrder} ${sqlLimit}`;
115
121
 
116
- if (sql) return dataQuery;
122
+ if (sql && process.platform === 'win32') {
123
+ return dataQuery;
124
+ }
117
125
 
118
126
  const rows = await pg.query(dataQuery, args.concat([limit, offset])).then(el => el.rows || []);
119
127
  const total = await pg.query(`SELECT COUNT(*) ${sqlBase}`, args).then(el => +(el.rows[0]?.count || 0));
@@ -1,15 +1,16 @@
1
1
  import { afterAll, beforeAll, describe, expect, test } from 'vitest';
2
2
 
3
- import { getPGAsync } from '@opengis/fastify-table/utils.js';
3
+ import { getPGAsync, getRedis } from '@opengis/fastify-table/utils.js';
4
4
 
5
5
  import { build, injectWithHeaders } from '../../../test/helper.js';
6
6
 
7
- let pg;
7
+ let pg, rclient;
8
8
 
9
9
  beforeAll(async () => {
10
10
  await build();
11
11
 
12
12
  pg = await getPGAsync();
13
+ rclient = getRedis();
13
14
 
14
15
  await cleanUp('before');
15
16
  });
@@ -80,7 +81,8 @@ describe('GIS routes', () => {
80
81
  const payload = {
81
82
  register_key: 'unit-test',
82
83
  name: 'unit-test',
83
- table_name: table
84
+ table_name: table,
85
+ filters: [{ name: pg.pk[table], ua: 'test filter', type: 'select' }]
84
86
  };
85
87
 
86
88
  const res = await injectWithHeaders({
@@ -130,6 +132,33 @@ describe('GIS routes', () => {
130
132
  expect(res.body.length).toBeGreaterThan(0);
131
133
  });
132
134
 
135
+ test('GET /gis-suggest/:token returns 200', async () => {
136
+ const { table, filter } = await pg.query('select table_name as table, filters->0->>\'name\' as filter from gis.registers where register_id=$1', [testId]).then((r) => r.rows[0]);
137
+ expect(filter).toBeTypeOf('string');
138
+ const hash = 'unit-test';
139
+ const redisKey = `${pg.options.database}:registry-filter:${hash}`;
140
+ await rclient.set(redisKey, JSON.stringify({ register: testId, filter }), 'EX', 1 * 60);
141
+ const res = await injectWithHeaders({
142
+ method: 'GET',
143
+ url: `/api/gis-suggest/${hash}`,
144
+ });
145
+ await rclient.del(redisKey);
146
+ expect(res.statusCode).toBe(200);
147
+ expect(res.body).toHaveProperty('time');
148
+ expect(res.body).toHaveProperty('limit');
149
+ expect(res.body).toHaveProperty('count');
150
+ expect(res.body).toHaveProperty('total');
151
+ expect(res.body).toHaveProperty('data');
152
+ expect(res.body.data).toBeInstanceOf(Array);
153
+ expect(res.body.data.length).toBeGreaterThan(0);
154
+ expect(res.body.data[0]).toHaveProperty('count');
155
+ expect(res.body.data[0]).toHaveProperty('id');
156
+ expect(res.body.data[0]).toHaveProperty('text');
157
+
158
+ const base64url = await pg.query(`select rtrim( replace( replace( replace( encode("${filter.replace(/"/g, '')}"::bytea, 'base64'), '+', '' ), '-', '' ), '/', '' ), '=' ) as id from ${table} where ${filter}::text=$1`, [res.body.data[0].text]).then((r) => r.rows[0].id);
159
+ expect(res.body.data[0].id).toBe(base64url);
160
+ });
161
+
133
162
  test('DELETE /gis-registry/:slug returns 200', async () => {
134
163
  const res = await injectWithHeaders({
135
164
  method: 'DELETE',
@@ -1,4 +1,8 @@
1
- const queryFilterSchema = { type: 'string', pattern: '^([\\w\\d_-]+)=([А-Яа-яҐґЄєІіЇї\\d\\w\\s\\/\\[\\]\\(\\)\\{\\}\\|,.!?;:—_=-@%#$&^*+=`~]+)$' };
1
+ const queryFilterSchema = {
2
+ type: ["string", "null"],
3
+ pattern:
4
+ '^([\\w-]+)=([А-Яа-яҐґЄєІіЇї\\w\\s\\\\/\\[\\]\\(\\)\\{\\}\\|,.!?;:—_=@%#$&^*+`~]*)$',
5
+ };
2
6
  const querySearchSchema = { type: 'string', pattern: '^([А-Яа-яҐґЄєІіЇї\\d\\w\\s\\/\\[\\]\\(\\)\\{\\}\\|,.!?;:—_=-@%#$&^*+=`~]+)$' };
3
7
 
4
8
  const keySchema = {
@@ -261,7 +265,8 @@ export const gisSuggestSchema = {
261
265
  additionalProperties: false,
262
266
  properties: {
263
267
  key: keySchema,
264
- val: { type: "string", pattern: "([\\d\\w]+)$" },
268
+ json: { type: 'integer', enum: [1] },
269
+ val: querySearchSchema,
265
270
  limit: { type: 'integer', minimum: 1, maximum: 100 },
266
271
  },
267
272
  },