api-foundry-query-engine 0.8.67__tar.gz → 0.8.69__tar.gz

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 (37) hide show
  1. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/PKG-INFO +1 -1
  2. api_foundry_query_engine-0.8.69/api_foundry_query_engine/__init__.py +1 -0
  3. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/sql_insert_query_handler.py +1 -1
  4. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/sql_query_handler.py +69 -2
  5. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/sql_select_query_handler.py +1 -1
  6. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/utils/api_model.py +5 -0
  7. api_foundry_query_engine-0.8.67/api_foundry_query_engine/__init__.py +0 -1
  8. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/.gitignore +0 -0
  9. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/LICENSE +0 -0
  10. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/.pre-commit-config.yaml +0 -0
  11. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/adapters/adapter.py +0 -0
  12. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/adapters/case_change_adapter.py +0 -0
  13. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/adapters/gateway_adapter.py +0 -0
  14. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/adapters/security_adapter.py +0 -0
  15. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/connectors/connection.py +0 -0
  16. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/connectors/connection_factory.py +0 -0
  17. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/connectors/oracle_connector.py +0 -0
  18. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/connectors/postgres_connection.py +0 -0
  19. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/batch_operation_handler.py +0 -0
  20. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/dao.py +0 -0
  21. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/operation_dao.py +0 -0
  22. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/sql_custom_query_handler.py +0 -0
  23. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/sql_delete_query_handler.py +0 -0
  24. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/sql_restore_query_handler.py +0 -0
  25. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/sql_subselect_query_handler.py +0 -0
  26. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/dao/sql_update_query_handler.py +0 -0
  27. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/lambda_handler.py +0 -0
  28. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/operation.py +0 -0
  29. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/services/service.py +0 -0
  30. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/services/transactional_service.py +0 -0
  31. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/utils/app_exception.py +0 -0
  32. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/utils/claims_check.py +0 -0
  33. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/utils/dependency_resolver.py +0 -0
  34. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/utils/logger.py +0 -0
  35. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/utils/reference_resolver.py +0 -0
  36. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/api_foundry_query_engine/utils/token_decoder.py +0 -0
  37. {api_foundry_query_engine-0.8.67 → api_foundry_query_engine-0.8.69}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: api-foundry-query-engine
3
- Version: 0.8.67
3
+ Version: 0.8.69
4
4
  Summary: The AWS lambda service handler use by the `api_foundry` project is a powerful tool designed to automate the deployment of REST APIs on AWS using Lambda services to access and interact with relational databases (RDBMS). This project leverages the OpenAPI specification to define and manage the APIs
5
5
  Project-URL: Documentation, https://github.com/DanRepik/api-foundry
6
6
  Project-URL: Source, https://github.com/DanRepik/api-foundry
@@ -0,0 +1 @@
1
+ __version__ = "0.8.69"
@@ -59,7 +59,7 @@ class SQLInsertSchemaQueryHandler(SQLSchemaQueryHandler):
59
59
  )
60
60
  # Combine both (union of read and write)
61
61
  combined = {**read_props, **write_props}
62
- filters = self.operation.metadata_params.get("_properties", ".*").split()
62
+ filters = self.tokenize_selector_filters(self.operation.metadata_params.get("_properties", ".*"))
63
63
  self._insert_selection_results = self.filter_and_prefix_keys(filters, combined)
64
64
  return self._insert_selection_results
65
65
 
@@ -97,12 +97,15 @@ RELATIONAL_TYPES = {
97
97
  "lt": "<",
98
98
  "le": "<=",
99
99
  "eq": "=",
100
+ "ne": "<>",
100
101
  "ge": ">=",
101
102
  "gt": ">",
102
103
  "in": "in",
103
104
  "not-in": "not-in",
104
105
  "between": "between",
105
106
  "not-between": "not-between",
107
+ "like": "like",
108
+ "not-like": "not-like",
106
109
  }
107
110
 
108
111
 
@@ -355,6 +358,8 @@ class SQLQueryHandler:
355
358
  self.placeholder(property, f"{placeholder_name}_{index}") for index, _ in enumerate(value_set)
356
359
  ]
357
360
  sql = f"{column} {'NOT ' if operand == 'not-in' else ''}IN ({', '.join(assignments)})" # noqa E501
361
+ elif operand in ["like", "not-like"]:
362
+ sql = f"{column} {'NOT ' if operand == 'not-like' else ''}LIKE {self.placeholder(property, str(placeholder_name))}"
358
363
  else:
359
364
  sql = f"{column} {operand} {self.placeholder(property, str(placeholder_name))}"
360
365
  return sql
@@ -385,11 +390,16 @@ class SQLQueryHandler:
385
390
  for index, item in enumerate(value_set):
386
391
  item_name = f"{placeholder_name}_{index}"
387
392
  placeholders[item_name] = property.convert_to_db_value(item)
393
+ elif operand in ["like", "not-like"]:
394
+ placeholders = {placeholder_name: self._normalize_like_pattern(value_str)}
388
395
  else:
389
396
  placeholders = {placeholder_name: property.convert_to_db_value(value_str)}
390
397
 
391
398
  return placeholders
392
399
 
400
+ def _normalize_like_pattern(self, value: str) -> str:
401
+ return value.replace("*", "%").replace("?", "_")
402
+
393
403
  def search_value_assignment(
394
404
  self, property: SchemaObjectProperty, value, prefix: Optional[str] = None
395
405
  ) -> tuple[str, dict]:
@@ -508,13 +518,70 @@ class SQLSchemaQueryHandler(SQLQueryHandler):
508
518
  log.info("selection_result")
509
519
  if not hasattr(self, "__selection_results"):
510
520
  log.info("prefix_map: %s", self.prefix_map)
511
- filters = self.operation.metadata_params.get("_properties", ".*").split()
521
+ filters = self.tokenize_selector_filters(self.operation.metadata_params.get("_properties", ".*"))
512
522
  allowed_properties = self.check_permissions(
513
523
  "read", self.schema_object.permissions, self.schema_object.properties
514
524
  )
515
525
  self.__selection_results = self.filter_and_prefix_keys(filters, allowed_properties)
516
526
  return self.__selection_results
517
527
 
528
+ @staticmethod
529
+ def tokenize_selector_filters(filter_str: str) -> List[str]:
530
+ """Split selector strings on commas or whitespace outside regex groups.
531
+
532
+ This preserves commas used inside regex syntax such as `{1,3}` or `[a,b]`
533
+ while allowing callers to pass selectors as either space-delimited,
534
+ comma-delimited, or mixed.
535
+ """
536
+ if not filter_str:
537
+ return []
538
+
539
+ filters: List[str] = []
540
+ current: List[str] = []
541
+ bracket_depth = 0
542
+ brace_depth = 0
543
+ paren_depth = 0
544
+ escaped = False
545
+
546
+ for char in filter_str:
547
+ if escaped:
548
+ current.append(char)
549
+ escaped = False
550
+ continue
551
+
552
+ if char == "\\":
553
+ current.append(char)
554
+ escaped = True
555
+ continue
556
+
557
+ if char == "[":
558
+ bracket_depth += 1
559
+ elif char == "]" and bracket_depth > 0:
560
+ bracket_depth -= 1
561
+ elif char == "{":
562
+ brace_depth += 1
563
+ elif char == "}" and brace_depth > 0:
564
+ brace_depth -= 1
565
+ elif char == "(":
566
+ paren_depth += 1
567
+ elif char == ")" and paren_depth > 0:
568
+ paren_depth -= 1
569
+
570
+ if char in {",", " ", "\t", "\n", "\r"} and not (bracket_depth or brace_depth or paren_depth):
571
+ token = "".join(current).strip()
572
+ if token:
573
+ filters.append(token)
574
+ current = []
575
+ continue
576
+
577
+ current.append(char)
578
+
579
+ token = "".join(current).strip()
580
+ if token:
581
+ filters.append(token)
582
+
583
+ return filters
584
+
518
585
  def _has_soft_delete_conflicts(self) -> Dict[str, bool]:
519
586
  """
520
587
  Detect conflicts between query parameters and soft delete exclusions.
@@ -626,7 +693,7 @@ class SQLSchemaQueryHandler(SQLQueryHandler):
626
693
  if (
627
694
  self.operation.action != "read"
628
695
  and isinstance(value, str)
629
- and re.match(r"^(lt|le|eq|ne|gt|ge|in|not-in|between|not-between)::(.+)$", value)
696
+ and re.match(r"^(lt|le|eq|ne|gt|ge|in|not-in|between|not-between|like|not-like)::(.+)$", value)
630
697
  and self.schema_object.concurrency_property
631
698
  ):
632
699
  raise ApplicationException(
@@ -303,7 +303,7 @@ class SQLSelectSchemaQueryHandler(SQLSchemaQueryHandler):
303
303
  def get_regex_map(self, filter_str: str) -> dict:
304
304
  result = {}
305
305
 
306
- for flt in filter_str.split():
306
+ for flt in self.tokenize_selector_filters(filter_str):
307
307
  parts = flt.split(":")
308
308
  entity = parts[0] if len(parts) > 1 else self.schema_object.api_name
309
309
  expression = parts[-1]
@@ -125,6 +125,11 @@ class SchemaObjectProperty:
125
125
 
126
126
  api_type = self.api_type if self.api_type is not None else "string"
127
127
 
128
+ # Preserve structured values so JSON aggregates are serialized as native
129
+ # arrays/objects instead of stringified Python representations.
130
+ if api_type in ["array", "object"]:
131
+ return value
132
+
128
133
  # Handle string types (including UUID which is represented as string in API)
129
134
  if api_type in ["string", "uuid"]:
130
135
  return str(value) if value is not None else None
@@ -1 +0,0 @@
1
- __version__ = "0.8.67"