jaaql-middleware-python 4.7.3__tar.gz → 4.8.2__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 (72) hide show
  1. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/PKG-INFO +1 -1
  2. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/constants.py +1 -1
  3. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/db/db_interface.py +2 -5
  4. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/db/db_utils.py +9 -2
  5. jaaql-middleware-python-4.8.2/jaaql/db/db_utils_no_circ.py +52 -0
  6. jaaql-middleware-python-4.8.2/jaaql/email/email_manager.py +147 -0
  7. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/base_controller.py +1 -1
  8. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/model.py +18 -115
  9. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/utilities/utils.py +5 -6
  10. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/utilities/utils_no_project_imports.py +7 -0
  11. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql_middleware_python.egg-info/PKG-INFO +1 -1
  12. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql_middleware_python.egg-info/SOURCES.txt +1 -0
  13. jaaql-middleware-python-4.7.3/jaaql/email/email_manager.py +0 -76
  14. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/LICENSE.txt +0 -0
  15. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/README.md +0 -0
  16. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/__init__.py +0 -0
  17. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/config/__init__.py +0 -0
  18. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/config/config-docker.ini +0 -0
  19. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/config/config-test.ini +0 -0
  20. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/config/config.ini +0 -0
  21. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/config_constants.py +0 -0
  22. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/db/__init__.py +0 -0
  23. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/db/db_pg_interface.py +0 -0
  24. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/documentation/__init__.py +0 -0
  25. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/documentation/documentation_internal.py +0 -0
  26. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/documentation/documentation_public.py +0 -0
  27. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/documentation/documentation_shared.py +0 -0
  28. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/email/__init__.py +0 -0
  29. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/email/email_manager_service.py +0 -0
  30. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/email/patch_ems.py +0 -0
  31. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/exceptions/__init__.py +0 -0
  32. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/exceptions/custom_http_status.py +0 -0
  33. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/exceptions/http_status_exception.py +0 -0
  34. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/exceptions/not_yet_implement_exception.py +0 -0
  35. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/interpreter/__init__.py +0 -0
  36. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/interpreter/interpret_jaaql.py +0 -0
  37. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/jaaql.py +0 -0
  38. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/migrations/__init__.py +0 -0
  39. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/migrations/migration_history.sql +0 -0
  40. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/migrations/migrations.py +0 -0
  41. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/__init__.py +0 -0
  42. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/base_model.py +0 -0
  43. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/controller.py +0 -0
  44. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/controller_interface.py +0 -0
  45. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/exception_queries.py +0 -0
  46. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/generated_queries.py +0 -0
  47. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/handmade_queries.py +0 -0
  48. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/model_interface.py +0 -0
  49. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/mvc/response.py +0 -0
  50. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/openapi/__init__.py +0 -0
  51. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/openapi/swagger_documentation.py +0 -0
  52. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/patch.py +0 -0
  53. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/scripts/01.install_domains.generated.sql +0 -0
  54. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/scripts/02.install_super_user.exceptions.sql +0 -0
  55. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/scripts/03.install_super_user.handwritten.sql +0 -0
  56. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/scripts/04.install_jaaql_data_structures.generated.sql +0 -0
  57. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/scripts/05.install_jaaql.exceptions.sql +0 -0
  58. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/scripts/06.install_jaaql.handwritten.sql +0 -0
  59. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/scripts/swagger_template.html +0 -0
  60. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/services/__init__.py +0 -0
  61. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/services/cached_canned_query_service.py +0 -0
  62. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/services/migrations_manager_service.py +0 -0
  63. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/services/patch_mms.py +0 -0
  64. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/utilities/__init__.py +0 -0
  65. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/utilities/crypt_utils.py +0 -0
  66. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/utilities/options.py +0 -0
  67. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql/utilities/vault.py +0 -0
  68. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql_middleware_python.egg-info/dependency_links.txt +0 -0
  69. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql_middleware_python.egg-info/requires.txt +0 -0
  70. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/jaaql_middleware_python.egg-info/top_level.txt +0 -0
  71. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/setup.cfg +0 -0
  72. {jaaql-middleware-python-4.7.3 → jaaql-middleware-python-4.8.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jaaql-middleware-python
3
- Version: 4.7.3
3
+ Version: 4.8.2
4
4
  Summary: The jaaql package, allowing for rapid development and deployment of RESTful HTTP applications
5
5
  Home-page: https://github.com/JAAQL/JAAQL-middleware-python
6
6
  Author: Software Quality Measurement and Improvement bv
@@ -128,5 +128,5 @@ ROLE__postgres = "postgres"
128
128
 
129
129
  PROTOCOL__postgres = "postgresql://"
130
130
 
131
- VERSION = "4.7.3"
131
+ VERSION = "4.8.2"
132
132
 
@@ -2,9 +2,9 @@ import traceback
2
2
  from abc import ABC, abstractmethod
3
3
  from datetime import datetime
4
4
  import logging
5
- from jaaql.utilities.utils_no_project_imports import time_delta_ms
6
5
  from jaaql.exceptions.http_status_exception import *
7
6
  from typing import Optional
7
+ from jaaql.utilities.utils_no_project_imports import objectify
8
8
  import queue
9
9
 
10
10
  ERR__unknown_echo = "Unknown echo type '%s'. Please use either %s"
@@ -159,13 +159,10 @@ class DBInterface(ABC):
159
159
  ret[RET__echo] = query
160
160
 
161
161
  if as_objects:
162
- return self.objectify(ret)
162
+ return objectify(ret)
163
163
  else:
164
164
  return ret
165
165
 
166
- def objectify(self, data):
167
- return [dict(zip(data['columns'], row)) for row in data['rows']]
168
-
169
166
  def execute_script_file(self, conn, file_loc: str = None, as_content: str = None, as_individual=False, commit=True):
170
167
  ret = None
171
168
  err = None
@@ -1,9 +1,10 @@
1
1
  from jaaql.exceptions.http_status_exception import HttpStatusException
2
2
  from jaaql.interpreter.interpret_jaaql import InterpretJAAQL
3
- from jaaql.constants import ENCODING__utf
3
+ from jaaql.constants import ENCODING__utf, VAULT_KEY__super_db_credentials
4
4
  from typing import Union
5
5
  import jaaql.utilities.crypt_utils as crypt_utils
6
6
  from jaaql.db.db_pg_interface import DBPGInterface
7
+ from jaaql.db.db_interface import DBInterface
7
8
 
8
9
  ERR__encryption_key_required = "Encryption key required. Check internal function calls"
9
10
  ERR__duplicated_encrypt_parameter = "Duplicated value in encrypt_parameters list"
@@ -12,7 +13,7 @@ ERR__missing_encrypt_parameter = "Encrypted parameter is not found '%s'"
12
13
  ERR__duplicated_encryption_salt = "Duplicated value in encryption_salts list"
13
14
  ERR__expected_single_row = "Expected single row response but received '%d' rows"
14
15
  ERR__unsupported_interface = "Unsupported interface '%s'. We only support %s"
15
-
16
+ ERR__schema_invalid = "Schema invalid!"
16
17
 
17
18
  KEY_CONFIG__db = "DATABASE"
18
19
  KEY_CONFIG__interface = "interface"
@@ -163,3 +164,9 @@ def execute_supplied_statements(db_interface, queries: Union[str, list],
163
164
  data = [db_interface.objectify(obj) for obj in data]
164
165
 
165
166
  return data
167
+
168
+
169
+ def create_interface_for_db(vault, config, user_id: str, database: str, sub_role: str = None):
170
+ jaaql_uri = vault.get_obj(VAULT_KEY__super_db_credentials)
171
+ address, port, _, username, password = DBInterface.fracture_uri(jaaql_uri)
172
+ return create_interface(config, address, port, database, username, password=password, role=user_id, sub_role=sub_role)
@@ -0,0 +1,52 @@
1
+ from jaaql.mvc.exception_queries import QUERY__fetch_application_schemas, KG__application_schema__application, KG__application__is_live, \
2
+ KG__application_schema__name, KEY__is_default
3
+ from queue import Queue
4
+ from jaaql.db.db_utils import execute_supplied_statement, create_interface_for_db, ERR__schema_invalid
5
+ from jaaql.exceptions.http_status_exception import HttpStatusException
6
+ from jaaql.interpreter.interpret_jaaql import InterpretJAAQL
7
+ from jaaql.constants import KEY__application, KEY__database, KEY__schema, KEY__role, DB__jaaql, \
8
+ KEY__read_only
9
+ from jaaql.db.db_interface import DBInterface
10
+
11
+
12
+ def submit(vault, config, db_crypt_key, jaaql_connection: DBInterface, inputs: dict, account_id: str, verification_hook: Queue = None,
13
+ cached_canned_query_service = None):
14
+ if not isinstance(inputs, dict):
15
+ raise HttpStatusException("Expected object or string input")
16
+
17
+ if KEY__application in inputs:
18
+ schemas = execute_supplied_statement(jaaql_connection, QUERY__fetch_application_schemas, {
19
+ KG__application_schema__application: inputs[KEY__application]
20
+ }, as_objects=True)
21
+ if len(schemas) == 0:
22
+ raise HttpStatusException("Application has no schemas!")
23
+ if not schemas[0][KG__application__is_live]:
24
+ raise HttpStatusException("Application is currently being deployed. Please wait a few minutes until deployment is complete")
25
+ schemas = {itm[KG__application_schema__name]: itm for itm in schemas}
26
+
27
+ found_db = None
28
+ if KEY__schema in inputs:
29
+ found_db = schemas[inputs[KEY__schema]][KEY__database]
30
+ inputs.pop(KEY__schema)
31
+ else:
32
+ if len(schemas) == 1:
33
+ found_db = schemas[list(schemas.keys())[0]][KEY__database]
34
+ else:
35
+ found_dbs = [val[KEY__database] for _, val in schemas.items() if val[KEY__is_default]]
36
+ if len(found_dbs) == 1:
37
+ found_db = found_dbs[0]
38
+
39
+ if not found_db:
40
+ raise HttpStatusException(ERR__schema_invalid)
41
+
42
+ inputs[KEY__database] = found_db
43
+
44
+ if KEY__database not in inputs:
45
+ inputs[KEY__database] = DB__jaaql
46
+
47
+ sub_role = inputs.pop(KEY__role) if KEY__role in inputs else None
48
+ required_db = create_interface_for_db(vault, config, account_id, inputs[KEY__database], sub_role)
49
+
50
+ return InterpretJAAQL(required_db).transform(inputs, skip_commit=inputs.get(KEY__read_only), wait_hook=verification_hook,
51
+ encryption_key=db_crypt_key,
52
+ canned_query_service=cached_canned_query_service)
@@ -0,0 +1,147 @@
1
+ import re
2
+
3
+ from typing import Callable
4
+ from jaaql.email.email_manager_service import Email
5
+ import requests
6
+ from jaaql.exceptions.http_status_exception import HttpStatusException
7
+ from jaaql.constants import *
8
+ from urllib.parse import quote
9
+ from jaaql.utilities.utils_no_project_imports import load_artifact
10
+ from typing import Optional
11
+ from jaaql.mvc.handmade_queries import *
12
+ from jaaql.db.db_utils_no_circ import submit
13
+ from jaaql.interpreter.interpret_jaaql import KEY_query, KEY_parameters, KEY_assert, ASSERT_one
14
+
15
+ REGEX__object_name = r'^[0-9a-zA-Z_]{1,63}$'
16
+
17
+ ERR__invalid_object_name = "Object name '%s' is invalid. Must match regex: " + REGEX__object_name
18
+
19
+ REGEX__email_parameter = r'({{)([a-zA-Z0-9_\-]+)(}})'
20
+ REGEX__email_uri_encoded_parameter = r'(\[\[)([a-zA-Z0-9_\-]+)(\]\])'
21
+ REPLACE__str = "{{%s}}"
22
+ REPLACE__uri_encoded_str = "[[%s]]"
23
+ ERR__missing_parameter = "Missing parameter from template '%s'"
24
+ ERR__unexpected_parameter_in_template = "Unexpected parameter in template '%s'"
25
+
26
+ EMAIL_HTML__start = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\r\n<body>\r\n"
27
+ EMAIL_HTML__end = "\r\n</body>\r\n</html>"
28
+
29
+ SUBJECT_MARKER = "Subject: "
30
+
31
+ ERR__unexpected_parameters = "Email parameters were not expected"
32
+ ERR__expected_parameters = "Email parameters were expected"
33
+
34
+
35
+ class EmailManager:
36
+
37
+ def __init__(self, is_container: bool):
38
+ self.is_container = is_container
39
+
40
+ def _send_email(self, email: Email):
41
+ requests.post("http://127.0.0.1:" + str(PORT__ems) + ENDPOINT__send_email, json=email.repr_json())
42
+
43
+ @staticmethod
44
+ def uri_encode_replace(val):
45
+ return quote(str(val))
46
+
47
+ def send_email(self, vault, config, db_crypt_key, jaaql_connection, application: str, template: str, application_artifacts_source: str,
48
+ application_base_url: str, account_id: str, parameters: dict = None, parameter_id: str = None, none_sanitized_parameters: dict = None):
49
+ if none_sanitized_parameters is None:
50
+ none_sanitized_parameters = {}
51
+
52
+ account = account__select(jaaql_connection, db_crypt_key, account_id)
53
+ template = email_template__select(jaaql_connection, application, template)
54
+
55
+ if parameters is not None and len(parameters) == 0:
56
+ parameters = None
57
+
58
+ if template[KG__email_template__validation_schema] is None and parameters is not None:
59
+ raise HttpStatusException(ERR__unexpected_parameters)
60
+ if template[KG__email_template__validation_schema] is not None and parameters is None:
61
+ raise HttpStatusException(ERR__expected_parameters)
62
+
63
+ if parameters is not None:
64
+ ins_query = "INSERT INTO %s (%s) VALUES (%s) RETURNING id"
65
+
66
+ if parameter_id is not None:
67
+ parameters[KEY__id] = parameter_id
68
+
69
+ for col, _ in parameters.items():
70
+ if not re.match(REGEX__object_name, col):
71
+ raise HttpStatusException(ERR__invalid_object_name % col)
72
+
73
+ cols = ", ".join(['"' + key + '"' for key in parameters.keys()])
74
+ vals = ", ".join([':' + key for key in parameters.keys()])
75
+
76
+ ins_query = ins_query % (template[KG__email_template__data_validation_table], cols, vals)
77
+ submit(vault, config, db_crypt_key, jaaql_connection, {
78
+ KEY__application: application,
79
+ KEY__schema: template[KG__email_template__validation_schema],
80
+ KEY_query: ins_query,
81
+ KEY_parameters: parameters,
82
+ KEY_assert: ASSERT_one
83
+ }, account_id)
84
+ parameter_id = execute_supplied_statement_singleton(jaaql_connection, ins_query, parameters, as_objects=True)[KEY__id]
85
+
86
+ sel_table = \
87
+ template[KG__email_template__data_validation_table] if template[KG__email_template__data_validation_view] is None \
88
+ else template[KG__email_template__data_validation_view]
89
+
90
+ sel_query = "SELECT * FROM %s WHERE id = :id" % sel_table
91
+ submit(vault, config, db_crypt_key, jaaql_connection, {
92
+ KEY__application: application,
93
+ KEY__schema: template[KG__email_template__validation_schema],
94
+ KEY_query: sel_query,
95
+ KEY_parameters: parameters,
96
+ KEY_assert: ASSERT_one
97
+ }, account_id)
98
+ parameters = execute_supplied_statement_singleton(jaaql_connection, sel_query, {KEY__id: parameter_id}, as_objects=True)
99
+ else:
100
+ parameters = {}
101
+
102
+ none_sanitized_parameters[EMAIL_PARAM__app_url] = application_base_url
103
+ none_sanitized_parameters[EMAIL_PARAM__email_address] = account[KG__account__username]
104
+ parameters = {**parameters, **none_sanitized_parameters}
105
+
106
+ self.construct_and_send_email(application_artifacts_source, template[KG__email_template__dispatcher], template,
107
+ account[KG__account__username], parameters)
108
+
109
+ def construct_and_send_email(self, application_artifacts_source: Optional[str], dispatcher: str, template: dict, to_email: str,
110
+ parameters: Optional[dict], attachments=None, attachment_access_token: str = None):
111
+ loaded_template = load_artifact(self.is_container, application_artifacts_source, template[KG__email_template__content_url])
112
+
113
+ loaded_template = self.perform_replacements(loaded_template, REPLACE__str, str, REGEX__email_parameter, parameters)
114
+ loaded_template = self.perform_replacements(loaded_template, REPLACE__uri_encoded_str, EmailManager.uri_encode_replace,
115
+ REGEX__email_uri_encoded_parameter, parameters)
116
+
117
+ first_line = loaded_template.split("\n")[0].strip()
118
+ if SUBJECT_MARKER not in first_line:
119
+ raise HttpStatusException("Email template does not have a subject on the first line denoted by 'Subject: '")
120
+ subject = first_line.split(SUBJECT_MARKER)[1].strip()
121
+ body = "\n".join(loaded_template.split("\n")[1:]).strip()
122
+
123
+ if template[KG__email_template__content_url].lower().endswith(".htmlbody"):
124
+ body = EMAIL_HTML__start + body + EMAIL_HTML__end
125
+
126
+ if attachments is not None:
127
+ attachment_list = attachments
128
+ if not isinstance(attachment_list, list):
129
+ attachment_list = [attachment_list]
130
+ for attachment in attachment_list:
131
+ attachment.format_attachment_url(application_artifacts_source, self.is_container)
132
+
133
+ self._send_email(Email(template[KEY__application], template[KG__email_template__name], dispatcher, to_email, subject, body,
134
+ attachments=attachments, attachment_access_token=attachment_access_token))
135
+
136
+ def perform_replacements(self, html_template: str, replace_str: str, replace_func: Callable, replace_regex: str, args: dict = None):
137
+ if args is None:
138
+ args = {}
139
+
140
+ for key, val in args.items():
141
+ html_template = html_template.replace(replace_str % key.upper(), replace_func(val))
142
+
143
+ matched = re.findall(replace_regex, html_template)
144
+ if len(matched) != 0:
145
+ raise HttpStatusException(ERR__missing_parameter % matched[0][1])
146
+
147
+ return html_template
@@ -17,7 +17,7 @@ from jaaql.constants import *
17
17
  from jaaql.mvc.model import JAAQLModel
18
18
  from jaaql.mvc.response import JAAQLResponse
19
19
  from typing import Union
20
- from jaaql.utilities.utils import create_interface_for_db
20
+ from jaaql.db.db_utils import create_interface_for_db
21
21
  from monitor.main import HEADER__security, HEADER__security_bypass_jaaql, HEADER__security_bypass
22
22
 
23
23
  from jaaql.openapi.swagger_documentation import SwaggerDocumentation, SwaggerMethod, TYPE__response, \
@@ -1,4 +1,3 @@
1
- import re
2
1
  import uuid
3
2
 
4
3
  from jaaql.db.db_pg_interface import DBPGInterface
@@ -8,9 +7,10 @@ from jaaql.exceptions.http_status_exception import HttpStatusException, HTTPStat
8
7
  from os.path import join
9
8
  from jaaql.constants import *
10
9
  from jaaql.mvc.response import JAAQLResponse
11
- from jaaql.interpreter.interpret_jaaql import InterpretJAAQL, ASSERT_one, KEY_assert, KEY_query, KEY_parameters
12
- from jaaql.utilities.utils import get_jaaql_root, get_base_url, create_interface_for_db
10
+ from jaaql.utilities.utils import get_jaaql_root, get_base_url
11
+ from jaaql.utilities.utils_no_project_imports import objectify
13
12
  from jaaql.db.db_utils import create_interface, jaaql__encrypt
13
+ from jaaql.db.db_utils_no_circ import submit
14
14
  from jaaql.utilities import crypt_utils
15
15
  import threading
16
16
  from datetime import datetime, timedelta
@@ -25,24 +25,18 @@ import random
25
25
 
26
26
  from jaaql.migrations.migrations import run_migrations
27
27
 
28
- REGEX__object_name = r'^[0-9a-zA-Z_]{1,63}$'
29
-
30
- ERR__invalid_object_name = "Object name '%s' is invalid. Must match regex: " + REGEX__object_name
31
28
  ERR__refresh_expired = "Token too old to be used for refresh. Please authenticate again"
32
29
  ERR__incorrect_install_key = "Incorrect install key!"
33
30
  ERR__invalid_level = "Invalid level!"
34
31
  ERR__incorrect_credentials = "Incorrect credentials!"
35
32
  ERR__email_template_not_installed = "Either email template does not exist"
36
33
  ERR__lacking_permissions = "Only an administrator can perform this action!"
37
- ERR__schema_invalid = "Schema invalid!"
38
34
  ERR__cant_send_attachments = "Cannot send attachments to other people"
39
35
  ERR__keep_alive_failed = "Keep alive failed"
40
36
  ERR__template_not_signup = "Sign up template does not have the correct type"
41
37
  ERR__template_not_already = "Already signed up template does not have the correct type"
42
38
  ERR__template_not_reset = "Reset template does not have the correct type"
43
39
  ERR__template_not_unregistered = "Unregistered template does not have the correct type"
44
- ERR__unexpected_parameters = "Email parameters were not expected"
45
- ERR__expected_parameters = "Email parameters were expected"
46
40
  ERR__unexpected_validation_column = "Unexpected column in the input parameters '%s'"
47
41
  ERR__data_validation_table_no_primary = "Data validation table has no primary key"
48
42
  ERR__cant_find_sign_up = "Cannot locate sign up with key. The key is either incorrect, has expired or has not been activated with the emailed code"
@@ -469,68 +463,6 @@ WHERE
469
463
  def attach_dispatcher_credentials(self, connection: DBInterface, inputs: dict):
470
464
  email_dispatcher__update(connection, self.get_db_crypt_key(), **inputs)
471
465
 
472
- def send_email(self, application: str, template: str, application_artifacts_source: str, application_base_url: str, account_id: str,
473
- parameters: dict = None, parameter_id: str = None, none_sanitized_parameters: dict = None):
474
- if none_sanitized_parameters is None:
475
- none_sanitized_parameters = {}
476
-
477
- account = account__select(self.jaaql_lookup_connection, self.get_db_crypt_key(), account_id)
478
- template = email_template__select(self.jaaql_lookup_connection, application, template)
479
-
480
- if parameters is not None and len(parameters) == 0:
481
- parameters = None
482
-
483
- if template[KG__email_template__validation_schema] is None and parameters is not None:
484
- raise HttpStatusException(ERR__unexpected_parameters)
485
- if template[KG__email_template__validation_schema] is not None and parameters is None:
486
- raise HttpStatusException(ERR__expected_parameters)
487
-
488
- if parameters is not None:
489
- ins_query = "INSERT INTO %s (%s) VALUES (%s) RETURNING id"
490
-
491
- if parameter_id is not None:
492
- parameters[KEY__id] = parameter_id
493
-
494
- for col, _ in parameters.items():
495
- if not re.match(REGEX__object_name, col):
496
- raise HttpStatusException(ERR__invalid_object_name % col)
497
-
498
- cols = ", ".join(['"' + key + '"' for key in parameters.keys()])
499
- vals = ", ".join([':' + key for key in parameters.keys()])
500
-
501
- ins_query = ins_query % (template[KG__email_template__data_validation_table], cols, vals)
502
- self.submit({
503
- KEY__application: application,
504
- KEY__schema: template[KG__email_template__validation_schema],
505
- KEY_query: ins_query,
506
- KEY_parameters: parameters,
507
- KEY_assert: ASSERT_one
508
- }, account_id)
509
- parameter_id = execute_supplied_statement_singleton(self.jaaql_lookup_connection, ins_query, parameters, as_objects=True)[KEY__id]
510
-
511
- sel_table = \
512
- template[KG__email_template__data_validation_table] if template[KG__email_template__data_validation_view] is None \
513
- else template[KG__email_template__data_validation_view]
514
-
515
- sel_query = "SELECT * FROM %s WHERE id = :id" % sel_table
516
- self.submit({
517
- KEY__application: application,
518
- KEY__schema: template[KG__email_template__validation_schema],
519
- KEY_query: sel_query,
520
- KEY_parameters: parameters,
521
- KEY_assert: ASSERT_one
522
- }, account_id)
523
- parameters = execute_supplied_statement_singleton(self.jaaql_lookup_connection, sel_query, {KEY__id: parameter_id}, as_objects=True)
524
- else:
525
- parameters = {}
526
-
527
- none_sanitized_parameters[EMAIL_PARAM__app_url] = application_base_url
528
- none_sanitized_parameters[EMAIL_PARAM__email_address] = account[KG__account__username]
529
- parameters = {**parameters, **none_sanitized_parameters}
530
-
531
- self.email_manager.construct_and_send_email(application_artifacts_source, template[KG__email_template__dispatcher], template,
532
- account[KG__account__username], parameters)
533
-
534
466
  def gen_security_event_unlock_code(self, codeset: str, length: int):
535
467
  return "".join([codeset[random.randint(0, len(codeset) - 1)] for _ in range(length)])
536
468
 
@@ -654,9 +586,11 @@ WHERE
654
586
  reg_env_ins = security_event__insert(self.jaaql_lookup_connection, inputs[KG__security_event__application], template, account_id,
655
587
  unlock_code)
656
588
 
657
- self.send_email(inputs[KG__security_event__application], template, app[KG__application__artifacts_source],
658
- app[KG__application__base_url], account_id, inputs[KEY__parameters],
659
- parameter_id=reg_env_ins[KG__security_event__event_lock], none_sanitized_parameters={
589
+ self.email_manager.send_email(self.vault, self.config, self.get_db_crypt_key(), self.jaaql_lookup_connection, self.get_db_crypt_key(),
590
+ inputs[KG__security_event__application], template,
591
+ app[KG__application__artifacts_source],
592
+ app[KG__application__base_url], account_id, inputs[KEY__parameters],
593
+ parameter_id=reg_env_ins[KG__security_event__event_lock], none_sanitized_parameters={
660
594
  EMAIL_PARAM__unlock_key: reg_env_ins[KG__security_event__unlock_key],
661
595
  EMAIL_PARAM__unlock_code: unlock_code
662
596
  })
@@ -722,9 +656,10 @@ WHERE
722
656
  reg_env_ins = security_event__insert(self.jaaql_lookup_connection, inputs[KG__security_event__application], template, account_id,
723
657
  unlock_code)
724
658
 
725
- self.send_email(inputs[KG__security_event__application], template, app[KG__application__artifacts_source],
726
- app[KG__application__base_url], account_id, inputs[KEY__parameters],
727
- parameter_id=reg_env_ins[KG__security_event__event_lock], none_sanitized_parameters={
659
+ self.email_manager.send_email(self.vault, self.config, self.get_db_crypt_key(), self.jaaql_lookup_connection, self.get_db_crypt_key(),
660
+ inputs[KG__security_event__application], template,
661
+ app[KG__application__artifacts_source], app[KG__application__base_url], account_id, inputs[KEY__parameters],
662
+ parameter_id=reg_env_ins[KG__security_event__event_lock], none_sanitized_parameters={
728
663
  EMAIL_PARAM__unlock_key: reg_env_ins[KG__security_event__unlock_key],
729
664
  EMAIL_PARAM__unlock_code: unlock_code
730
665
  })
@@ -733,43 +668,11 @@ WHERE
733
668
  KG__security_event__event_lock: reg_env_ins[KG__security_event__event_lock]
734
669
  }
735
670
 
736
- def submit(self, inputs: dict, account_id: str, verification_hook: Queue = None):
737
- if not isinstance(inputs, dict):
738
- raise HttpStatusException("Expected object or string input")
739
-
740
- if KEY__application in inputs:
741
- schemas = execute_supplied_statement(self.jaaql_lookup_connection, QUERY__fetch_application_schemas, {
742
- KG__application_schema__application: inputs[KEY__application]
743
- }, as_objects=True)
744
- if len(schemas) == 0:
745
- raise HttpStatusException("Application has no schemas!")
746
- if not schemas[0][KG__application__is_live]:
747
- raise HttpStatusException("Application is currently being deployed. Please wait a few minutes until deployment is complete")
748
- schemas = {itm[KG__application_schema__name]: itm for itm in schemas}
749
-
750
- found_db = None
751
- if KEY__schema in inputs:
752
- found_db = schemas[inputs[KEY__schema]][KEY__database]
753
- inputs.pop(KEY__schema)
754
- else:
755
- if len(schemas) == 1:
756
- found_db = schemas[list(schemas.keys())[0]][KEY__database]
757
- else:
758
- found_dbs = [val[KEY__database] for _, val in schemas.items() if val[KEY__is_default]]
759
- if len(found_dbs) == 1:
760
- found_db = found_dbs[0]
761
-
762
- if not found_db:
763
- raise HttpStatusException(ERR__schema_invalid)
764
-
765
- inputs[KEY__database] = found_db
766
-
767
- if KEY__database not in inputs:
768
- inputs[KEY__database] = DB__jaaql
671
+ def submit(self, inputs: dict, account_id: str, verification_hook: Queue = None, as_objects: bool = False, singleton: bool = False):
672
+ ret = submit(self.vault, self.config, self.get_db_crypt_key(), self.jaaql_lookup_connection, inputs, account_id, verification_hook,
673
+ self.cached_canned_query_service)
769
674
 
770
- sub_role = inputs.pop(KEY__role) if KEY__role in inputs else None
771
- required_db = create_interface_for_db(self.vault, self.config, account_id, inputs[KEY__database], sub_role)
675
+ if as_objects:
676
+ ret = objectify(ret, singleton=singleton)
772
677
 
773
- return InterpretJAAQL(required_db).transform(inputs, skip_commit=inputs.get(KEY__read_only), wait_hook=verification_hook,
774
- encryption_key=self.get_db_crypt_key(),
775
- canned_query_service=self.cached_canned_query_service)
678
+ return ret
@@ -3,12 +3,13 @@ from os.path import join, exists, dirname
3
3
  from datetime import datetime
4
4
  import os
5
5
  import glob
6
- from jaaql.constants import DIR__config, FILE__config, CONFIG_KEY__server, CONFIG_KEY_SERVER__port, ENVIRON__install_path, PORT__ems, PORT__mms,\
7
- VAULT_KEY__super_db_credentials
6
+ from jaaql.constants import DIR__config, FILE__config, CONFIG_KEY__server, CONFIG_KEY_SERVER__port, ENVIRON__install_path, PORT__ems, PORT__mms, \
7
+ VAULT_KEY__db_crypt_key
8
8
  from jaaql.config_constants import *
9
9
  from jaaql.db.db_interface import DBInterface
10
10
  from jaaql.db.db_utils import create_interface
11
11
  from jaaql.constants import VAULT_KEY__jaaql_lookup_connection
12
+ from jaaql.utilities.crypt_utils import ENCODING__ascii
12
13
  import configparser
13
14
  import time
14
15
  import urllib.parse
@@ -150,7 +151,5 @@ def get_db_connection_as_jaaql(config, vault, db: str):
150
151
  return create_interface(config, address, port, db, username, password)
151
152
 
152
153
 
153
- def create_interface_for_db(vault, config, user_id: str, database: str, sub_role: str = None):
154
- jaaql_uri = vault.get_obj(VAULT_KEY__super_db_credentials)
155
- address, port, _, username, password = DBInterface.fracture_uri(jaaql_uri)
156
- return create_interface(config, address, port, database, username, password=password, role=user_id, sub_role=sub_role)
154
+ def get_db_crypt_key(vault):
155
+ return vault.get_obj(VAULT_KEY__db_crypt_key).encode(ENCODING__ascii)
@@ -10,6 +10,13 @@ import re
10
10
  ALLOWABLE_FILE_PATH = r'^[a-z0-9_\-\/]+(\.[a-zA-Z0-9]+)?$'
11
11
 
12
12
 
13
+ def objectify(data, singleton: bool = False):
14
+ if singleton:
15
+ return dict(zip(data['columns'], data['rows'][0]))
16
+ else:
17
+ return [dict(zip(data['columns'], row)) for row in data['rows']]
18
+
19
+
13
20
  def time_delta_ms(start_time: datetime, end_time: datetime) -> int:
14
21
  return int(round((end_time - start_time).total_seconds() * 1000))
15
22
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jaaql-middleware-python
3
- Version: 4.7.3
3
+ Version: 4.8.2
4
4
  Summary: The jaaql package, allowing for rapid development and deployment of RESTful HTTP applications
5
5
  Home-page: https://github.com/JAAQL/JAAQL-middleware-python
6
6
  Author: Software Quality Measurement and Improvement bv
@@ -14,6 +14,7 @@ jaaql/db/__init__.py
14
14
  jaaql/db/db_interface.py
15
15
  jaaql/db/db_pg_interface.py
16
16
  jaaql/db/db_utils.py
17
+ jaaql/db/db_utils_no_circ.py
17
18
  jaaql/documentation/__init__.py
18
19
  jaaql/documentation/documentation_internal.py
19
20
  jaaql/documentation/documentation_public.py
@@ -1,76 +0,0 @@
1
- import re
2
-
3
- from typing import Callable
4
- from jaaql.email.email_manager_service import Email
5
- import requests
6
- from jaaql.exceptions.http_status_exception import HttpStatusException
7
- from jaaql.constants import *
8
- from urllib.parse import quote
9
- from jaaql.utilities.utils_no_project_imports import load_artifact
10
- from typing import Optional
11
- from jaaql.mvc.handmade_queries import *
12
-
13
- REGEX__email_parameter = r'({{)([a-zA-Z0-9_\-]+)(}})'
14
- REGEX__email_uri_encoded_parameter = r'(\[\[)([a-zA-Z0-9_\-]+)(\]\])'
15
- REPLACE__str = "{{%s}}"
16
- REPLACE__uri_encoded_str = "[[%s]]"
17
- ERR__missing_parameter = "Missing parameter from template '%s'"
18
- ERR__unexpected_parameter_in_template = "Unexpected parameter in template '%s'"
19
-
20
- EMAIL_HTML__start = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\r\n<body>\r\n"
21
- EMAIL_HTML__end = "\r\n</body>\r\n</html>"
22
-
23
- SUBJECT_MARKER = "Subject: "
24
-
25
-
26
- class EmailManager:
27
-
28
- def __init__(self, is_container: bool):
29
- self.is_container = is_container
30
-
31
- def send_email(self, email: Email):
32
- requests.post("http://127.0.0.1:" + str(PORT__ems) + ENDPOINT__send_email, json=email.repr_json())
33
-
34
- @staticmethod
35
- def uri_encode_replace(val):
36
- return quote(str(val))
37
-
38
- def construct_and_send_email(self, application_artifacts_source: Optional[str], dispatcher: str, template: dict, to_email: str,
39
- parameters: Optional[dict], attachments=None, attachment_access_token: str = None):
40
- loaded_template = load_artifact(self.is_container, application_artifacts_source, template[KG__email_template__content_url])
41
-
42
- loaded_template = self.perform_replacements(loaded_template, REPLACE__str, str, REGEX__email_parameter, parameters)
43
- loaded_template = self.perform_replacements(loaded_template, REPLACE__uri_encoded_str, EmailManager.uri_encode_replace,
44
- REGEX__email_uri_encoded_parameter, parameters)
45
-
46
- first_line = loaded_template.split("\n")[0].strip()
47
- if SUBJECT_MARKER not in first_line:
48
- raise HttpStatusException("Email template does not have a subject on the first line denoted by 'Subject: '")
49
- subject = first_line.split(SUBJECT_MARKER)[1].strip()
50
- body = "\n".join(loaded_template.split("\n")[1:]).strip()
51
-
52
- if template[KG__email_template__content_url].lower().endswith(".htmlbody"):
53
- body = EMAIL_HTML__start + body + EMAIL_HTML__end
54
-
55
- if attachments is not None:
56
- attachment_list = attachments
57
- if not isinstance(attachment_list, list):
58
- attachment_list = [attachment_list]
59
- for attachment in attachment_list:
60
- attachment.format_attachment_url(application_artifacts_source, self.is_container)
61
-
62
- self.send_email(Email(template[KEY__application], template[KG__email_template__name], dispatcher, to_email, subject, body,
63
- attachments=attachments, attachment_access_token=attachment_access_token))
64
-
65
- def perform_replacements(self, html_template: str, replace_str: str, replace_func: Callable, replace_regex: str, args: dict = None):
66
- if args is None:
67
- args = {}
68
-
69
- for key, val in args.items():
70
- html_template = html_template.replace(replace_str % key.upper(), replace_func(val))
71
-
72
- matched = re.findall(replace_regex, html_template)
73
- if len(matched) != 0:
74
- raise HttpStatusException(ERR__missing_parameter % matched[0][1])
75
-
76
- return html_template