velocity-python 0.0.78__tar.gz → 0.0.80__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.
Potentially problematic release.
This version of velocity-python might be problematic. Click here for more details.
- {velocity_python-0.0.78 → velocity_python-0.0.80}/PKG-INFO +2 -1
- {velocity_python-0.0.78 → velocity_python-0.0.80}/pyproject.toml +2 -1
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/__init__.py +1 -1
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/aws/__init__.py +2 -0
- velocity_python-0.0.80/src/velocity/aws/amplify.py +422 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity_python.egg-info/PKG-INFO +2 -1
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity_python.egg-info/SOURCES.txt +1 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity_python.egg-info/requires.txt +1 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/LICENSE +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/README.md +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/setup.cfg +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/aws/handlers/context.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/aws/handlers/lambda_handler.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/aws/handlers/sqs_handler.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/__init__.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/decorators.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/engine.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/exceptions.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/result.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/row.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/table.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/core/transaction.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/mysql.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/mysql_reserved.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/postgres/__init__.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/postgres/sql.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/sqlite.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/sqlite_reserved.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/sqlserver.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/sqlserver_reserved.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/tablehelper.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/__init__.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity_python.egg-info/top_level.txt +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_db.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_email_processing.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_format.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_iconv.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_merge.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_oconv.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_postgres.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_response.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_sql_builder.py +0 -0
- {velocity_python-0.0.78 → velocity_python-0.0.80}/tests/test_timer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: velocity-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.80
|
|
4
4
|
Summary: A rapid application development library for interfacing with data storage
|
|
5
5
|
Author-email: Paul Perez <pperez@codeclubs.org>
|
|
6
6
|
Project-URL: Homepage, https://codeclubs.org/projects/velocity
|
|
@@ -14,6 +14,7 @@ Requires-Dist: requests
|
|
|
14
14
|
Requires-Dist: jinja2
|
|
15
15
|
Requires-Dist: xlrd
|
|
16
16
|
Requires-Dist: openpyxl
|
|
17
|
+
Requires-Dist: sqlparse
|
|
17
18
|
Provides-Extra: mysql
|
|
18
19
|
Requires-Dist: mysql-connector-python; extra == "mysql"
|
|
19
20
|
Provides-Extra: sqlserver
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "velocity-python"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.80"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="Paul Perez", email="pperez@codeclubs.org" },
|
|
6
6
|
]
|
|
@@ -17,6 +17,7 @@ dependencies = [
|
|
|
17
17
|
'jinja2',
|
|
18
18
|
'xlrd',
|
|
19
19
|
'openpyxl',
|
|
20
|
+
'sqlparse'
|
|
20
21
|
]
|
|
21
22
|
|
|
22
23
|
[project.urls]
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AmplifyProject:
|
|
5
|
+
def __init__(self, app_id: str):
|
|
6
|
+
self.app_id = app_id
|
|
7
|
+
self.amplify_client = boto3.client("amplify")
|
|
8
|
+
self.lambda_client = boto3.client("lambda")
|
|
9
|
+
self.sqs_client = boto3.client("sqs")
|
|
10
|
+
self.sqs_resource = boto3.resource("sqs")
|
|
11
|
+
|
|
12
|
+
# Verify app exists
|
|
13
|
+
try:
|
|
14
|
+
response = self.amplify_client.get_app(appId=self.app_id)
|
|
15
|
+
self.app_name = response["app"]["name"]
|
|
16
|
+
except self.amplify_client.exceptions.NotFoundException:
|
|
17
|
+
raise ValueError(f"Amplify app with ID '{self.app_id}' does not exist.")
|
|
18
|
+
|
|
19
|
+
def get_app_name(self):
|
|
20
|
+
return self.app_name
|
|
21
|
+
|
|
22
|
+
def get_region(self):
|
|
23
|
+
return self.amplify_client.meta.region_name
|
|
24
|
+
|
|
25
|
+
def get_account_id(self):
|
|
26
|
+
sts_client = boto3.client("sts")
|
|
27
|
+
return sts_client.get_caller_identity()["Account"]
|
|
28
|
+
|
|
29
|
+
def list_backend_branches(self):
|
|
30
|
+
branches = []
|
|
31
|
+
paginator = self.amplify_client.get_paginator("list_branches")
|
|
32
|
+
for page in paginator.paginate(appId=self.app_id):
|
|
33
|
+
for branch in page.get("branches", []):
|
|
34
|
+
branches.append(branch["branchName"])
|
|
35
|
+
return branches
|
|
36
|
+
|
|
37
|
+
def filtered_env_vars(self, raw_env_vars):
|
|
38
|
+
return {k: v for k, v in raw_env_vars.items() if not k.startswith("_")}
|
|
39
|
+
|
|
40
|
+
def get_merged_env_vars(self, branch):
|
|
41
|
+
all_vars = {}
|
|
42
|
+
|
|
43
|
+
# Global env vars
|
|
44
|
+
app_response = self.amplify_client.get_app(appId=self.app_id)
|
|
45
|
+
global_vars = self.filtered_env_vars(
|
|
46
|
+
app_response["app"].get("environmentVariables", {})
|
|
47
|
+
)
|
|
48
|
+
# print("📦 Global env vars (All branches):")
|
|
49
|
+
# for k, v in global_vars.items():
|
|
50
|
+
# print(f" {k}: {v}")
|
|
51
|
+
all_vars.update(global_vars)
|
|
52
|
+
|
|
53
|
+
# Branch-specific vars
|
|
54
|
+
try:
|
|
55
|
+
branch_response = self.amplify_client.get_branch(
|
|
56
|
+
appId=self.app_id, branchName=branch
|
|
57
|
+
)
|
|
58
|
+
branch_vars = self.filtered_env_vars(
|
|
59
|
+
branch_response["branch"].get("environmentVariables", {})
|
|
60
|
+
)
|
|
61
|
+
# print(f"🌿 Branch-specific env vars for '{branch}':")
|
|
62
|
+
# for k, v in branch_vars.items():
|
|
63
|
+
# print(f" {k}: {v}")
|
|
64
|
+
all_vars.update(branch_vars)
|
|
65
|
+
except self.amplify_client.exceptions.BadRequestException:
|
|
66
|
+
print(f"⚠️ Branch '{branch}' not found. Skipping branch-level overrides.")
|
|
67
|
+
|
|
68
|
+
return all_vars
|
|
69
|
+
|
|
70
|
+
def list_lambda_functions_filtered(self, env_name):
|
|
71
|
+
paginator = self.lambda_client.get_paginator("list_functions")
|
|
72
|
+
for page in paginator.paginate():
|
|
73
|
+
for fn in page["Functions"]:
|
|
74
|
+
name = fn["FunctionName"]
|
|
75
|
+
if self.app_name in name and env_name in name:
|
|
76
|
+
yield fn
|
|
77
|
+
|
|
78
|
+
def update_lambda_function(
|
|
79
|
+
self,
|
|
80
|
+
function_name,
|
|
81
|
+
replace_env_vars=None,
|
|
82
|
+
merge_env_vars=None,
|
|
83
|
+
replace_vpc_config=None,
|
|
84
|
+
merge_vpc_config=None,
|
|
85
|
+
subnet_ids=None,
|
|
86
|
+
security_group_ids=None,
|
|
87
|
+
description=None,
|
|
88
|
+
role=None,
|
|
89
|
+
handler=None,
|
|
90
|
+
runtime=None,
|
|
91
|
+
timeout=None,
|
|
92
|
+
memory_size=None,
|
|
93
|
+
dead_letter_config=None,
|
|
94
|
+
kms_key_arn=None,
|
|
95
|
+
tracing_config=None,
|
|
96
|
+
revision_id=None,
|
|
97
|
+
layers=None,
|
|
98
|
+
file_system_configs=None,
|
|
99
|
+
image_config=None,
|
|
100
|
+
ephemeral_storage=None,
|
|
101
|
+
environment_secrets=None,
|
|
102
|
+
):
|
|
103
|
+
# Fetch existing configuration
|
|
104
|
+
config = self.lambda_client.get_function_configuration(
|
|
105
|
+
FunctionName=function_name
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
update_params = {"FunctionName": function_name}
|
|
109
|
+
|
|
110
|
+
# Environment variables: replace OR merge
|
|
111
|
+
if replace_env_vars is not None and merge_env_vars is not None:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"Cannot specify both replace_env_vars and merge_env_vars at the same time."
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if replace_env_vars is not None:
|
|
117
|
+
update_params["Environment"] = {"Variables": replace_env_vars}
|
|
118
|
+
elif merge_env_vars is not None:
|
|
119
|
+
existing_env = config.get("Environment", {}).get("Variables", {})
|
|
120
|
+
merged_env = {**existing_env, **merge_env_vars}
|
|
121
|
+
update_params["Environment"] = {"Variables": merged_env}
|
|
122
|
+
|
|
123
|
+
# Environment secrets (always merge style)
|
|
124
|
+
if environment_secrets is not None:
|
|
125
|
+
existing_secrets = config.get("Environment", {}).get("Secrets", {})
|
|
126
|
+
merged_secrets = {**existing_secrets, **environment_secrets}
|
|
127
|
+
if "Environment" not in update_params:
|
|
128
|
+
update_params["Environment"] = {}
|
|
129
|
+
update_params["Environment"]["Secrets"] = merged_secrets
|
|
130
|
+
|
|
131
|
+
# VPC config: replace OR merge
|
|
132
|
+
if replace_vpc_config is not None and merge_vpc_config is not None:
|
|
133
|
+
raise ValueError(
|
|
134
|
+
"Cannot specify both replace_vpc_config and merge_vpc_config at the same time."
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
vpc_config_to_apply = None
|
|
138
|
+
|
|
139
|
+
if replace_vpc_config is not None:
|
|
140
|
+
vpc_config_to_apply = replace_vpc_config
|
|
141
|
+
else:
|
|
142
|
+
# Start with existing or empty
|
|
143
|
+
existing_vpc = config.get("VpcConfig", {})
|
|
144
|
+
# print(f"Existing VPC config for {function_name}: {existing_vpc}")
|
|
145
|
+
if "VpcId" in existing_vpc:
|
|
146
|
+
del existing_vpc["VpcId"] # Remove VpcId if present
|
|
147
|
+
vpc_config_to_apply = dict(existing_vpc)
|
|
148
|
+
|
|
149
|
+
if merge_vpc_config is not None:
|
|
150
|
+
vpc_config_to_apply.update(merge_vpc_config)
|
|
151
|
+
|
|
152
|
+
if subnet_ids is not None:
|
|
153
|
+
vpc_config_to_apply["SubnetIds"] = subnet_ids
|
|
154
|
+
|
|
155
|
+
if security_group_ids is not None:
|
|
156
|
+
vpc_config_to_apply["SecurityGroupIds"] = security_group_ids
|
|
157
|
+
|
|
158
|
+
# If vpc_config_to_apply has any keys, add to update_params
|
|
159
|
+
if vpc_config_to_apply and any(vpc_config_to_apply.values()):
|
|
160
|
+
update_params["VpcConfig"] = vpc_config_to_apply
|
|
161
|
+
|
|
162
|
+
# Other parameters
|
|
163
|
+
if description is not None:
|
|
164
|
+
update_params["Description"] = description
|
|
165
|
+
|
|
166
|
+
if role is not None:
|
|
167
|
+
update_params["Role"] = role
|
|
168
|
+
|
|
169
|
+
if handler is not None:
|
|
170
|
+
update_params["Handler"] = handler
|
|
171
|
+
|
|
172
|
+
if runtime is not None:
|
|
173
|
+
update_params["Runtime"] = runtime
|
|
174
|
+
|
|
175
|
+
if timeout is not None:
|
|
176
|
+
update_params["Timeout"] = timeout
|
|
177
|
+
|
|
178
|
+
if memory_size is not None:
|
|
179
|
+
update_params["MemorySize"] = memory_size
|
|
180
|
+
|
|
181
|
+
if dead_letter_config is not None:
|
|
182
|
+
update_params["DeadLetterConfig"] = dead_letter_config
|
|
183
|
+
|
|
184
|
+
if kms_key_arn is not None:
|
|
185
|
+
update_params["KMSKeyArn"] = kms_key_arn
|
|
186
|
+
|
|
187
|
+
if tracing_config is not None:
|
|
188
|
+
update_params["TracingConfig"] = tracing_config
|
|
189
|
+
|
|
190
|
+
if revision_id is not None:
|
|
191
|
+
update_params["RevisionId"] = revision_id
|
|
192
|
+
|
|
193
|
+
if layers is not None:
|
|
194
|
+
update_params["Layers"] = layers
|
|
195
|
+
|
|
196
|
+
if file_system_configs is not None:
|
|
197
|
+
update_params["FileSystemConfigs"] = file_system_configs
|
|
198
|
+
|
|
199
|
+
if image_config is not None:
|
|
200
|
+
update_params["ImageConfig"] = image_config
|
|
201
|
+
|
|
202
|
+
if ephemeral_storage is not None:
|
|
203
|
+
update_params["EphemeralStorage"] = ephemeral_storage
|
|
204
|
+
|
|
205
|
+
# Call update if needed
|
|
206
|
+
if len(update_params) > 1: # FunctionName is always present
|
|
207
|
+
self.lambda_client.update_function_configuration(**update_params)
|
|
208
|
+
else:
|
|
209
|
+
print(f"No updates provided for Lambda function '{function_name}'.")
|
|
210
|
+
|
|
211
|
+
def set_environment_variable(self, key: str, value: str, branch: str = None):
|
|
212
|
+
if branch:
|
|
213
|
+
response = self.amplify_client.get_branch(
|
|
214
|
+
appId=self.app_id, branchName=branch
|
|
215
|
+
)
|
|
216
|
+
env_vars = response["branch"].get("environmentVariables", {})
|
|
217
|
+
env_vars[key] = value
|
|
218
|
+
self.amplify_client.update_branch(
|
|
219
|
+
appId=self.app_id, branchName=branch, environmentVariables=env_vars
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
response = self.amplify_client.get_app(appId=self.app_id)
|
|
223
|
+
env_vars = response["app"].get("environmentVariables", {})
|
|
224
|
+
env_vars[key] = value
|
|
225
|
+
self.amplify_client.update_app(
|
|
226
|
+
appId=self.app_id, environmentVariables=env_vars
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def update_custom_redirect_rules(self):
|
|
230
|
+
srv_list = [
|
|
231
|
+
"css",
|
|
232
|
+
"gif",
|
|
233
|
+
"ico",
|
|
234
|
+
"jpg",
|
|
235
|
+
"js",
|
|
236
|
+
"json",
|
|
237
|
+
"map",
|
|
238
|
+
"otf",
|
|
239
|
+
"png",
|
|
240
|
+
"svg",
|
|
241
|
+
"ttf",
|
|
242
|
+
"txt",
|
|
243
|
+
"webp",
|
|
244
|
+
"woff",
|
|
245
|
+
"xml",
|
|
246
|
+
"pdf",
|
|
247
|
+
]
|
|
248
|
+
self.amplify_client.update_app(
|
|
249
|
+
appId=self.app_id,
|
|
250
|
+
customRules=[
|
|
251
|
+
{"source": "/<*>", "target": "/index.html", "status": "404-200"},
|
|
252
|
+
{
|
|
253
|
+
"source": f'</^[^.]+$|\.(?!({"|".join(srv_list)})$)([^.]+$)/>',
|
|
254
|
+
"target": "/",
|
|
255
|
+
"status": "200",
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def get_sqs_policy_template(
|
|
261
|
+
self,
|
|
262
|
+
queue_name: str,
|
|
263
|
+
queue_producers: str,
|
|
264
|
+
queue_handler: str,
|
|
265
|
+
) -> str:
|
|
266
|
+
account_id = self.get_account_id()
|
|
267
|
+
region = self.get_region()
|
|
268
|
+
roles = [qp["Role"] for qp in queue_producers]
|
|
269
|
+
return f"""
|
|
270
|
+
{{
|
|
271
|
+
"Version": "2008-10-17",
|
|
272
|
+
"Id": "__default_policy_ID",
|
|
273
|
+
"Statement": [
|
|
274
|
+
{{
|
|
275
|
+
"Sid": "__owner_statement",
|
|
276
|
+
"Effect": "Allow",
|
|
277
|
+
"Principal": {{
|
|
278
|
+
"AWS": "arn:aws:iam::{account_id}:root"
|
|
279
|
+
}},
|
|
280
|
+
"Action": "SQS:*",
|
|
281
|
+
"Resource": "arn:aws:sqs:{region}:{account_id}:{queue_name}"
|
|
282
|
+
}},
|
|
283
|
+
{{
|
|
284
|
+
"Sid": "__sender_statement",
|
|
285
|
+
"Effect": "Allow",
|
|
286
|
+
"Principal": {{
|
|
287
|
+
"AWS": {repr(roles).replace("'", '"')}
|
|
288
|
+
}},
|
|
289
|
+
"Action": [
|
|
290
|
+
"SQS:GetQueueAttributes",
|
|
291
|
+
"SQS:SendMessage"
|
|
292
|
+
],
|
|
293
|
+
"Resource": "arn:aws:sqs:{region}:{account_id}:{queue_name}"
|
|
294
|
+
}},
|
|
295
|
+
{{
|
|
296
|
+
"Sid": "__receiver_statement",
|
|
297
|
+
"Effect": "Allow",
|
|
298
|
+
"Principal": {{
|
|
299
|
+
"AWS": "{queue_handler["Role"]}"
|
|
300
|
+
}},
|
|
301
|
+
"Action": [
|
|
302
|
+
"SQS:GetQueueAttributes",
|
|
303
|
+
"SQS:ChangeMessageVisibility",
|
|
304
|
+
"SQS:DeleteMessage",
|
|
305
|
+
"SQS:ReceiveMessage"
|
|
306
|
+
],
|
|
307
|
+
"Resource": "arn:aws:sqs:{region}:{account_id}:{queue_name}"
|
|
308
|
+
}}
|
|
309
|
+
]
|
|
310
|
+
}}"""
|
|
311
|
+
|
|
312
|
+
def setup_sqs_queue_and_permissions(
|
|
313
|
+
self,
|
|
314
|
+
branch: str,
|
|
315
|
+
queue_name: str,
|
|
316
|
+
queue_producers: list,
|
|
317
|
+
queue_handler: object,
|
|
318
|
+
use_reserved_concurrency: bool = True,
|
|
319
|
+
):
|
|
320
|
+
account_id = self.get_account_id()
|
|
321
|
+
region = self.get_region()
|
|
322
|
+
|
|
323
|
+
policy_json = self.get_sqs_policy_template(
|
|
324
|
+
queue_name,
|
|
325
|
+
queue_producers,
|
|
326
|
+
queue_handler,
|
|
327
|
+
)
|
|
328
|
+
# print(f"Setting up SQS queue '{queue_name}' with policy:\n{policy_json}")
|
|
329
|
+
try:
|
|
330
|
+
queue = self.sqs_resource.get_queue_by_name(QueueName=queue_name)
|
|
331
|
+
self.sqs_client.set_queue_attributes(
|
|
332
|
+
QueueUrl=queue.url,
|
|
333
|
+
Attributes={"Policy": policy_json, "VisibilityTimeout": "900"},
|
|
334
|
+
)
|
|
335
|
+
except self.sqs_client.exceptions.QueueDoesNotExist:
|
|
336
|
+
queue = self.sqs_resource.create_queue(
|
|
337
|
+
QueueName=queue_name,
|
|
338
|
+
Attributes={"Policy": policy_json, "VisibilityTimeout": "900"},
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
try:
|
|
342
|
+
self.lambda_client.create_event_source_mapping(
|
|
343
|
+
EventSourceArn=f"arn:aws:sqs:{region}:{account_id}:{queue_name}",
|
|
344
|
+
FunctionName=queue_handler["FunctionName"],
|
|
345
|
+
Enabled=True,
|
|
346
|
+
BatchSize=1,
|
|
347
|
+
)
|
|
348
|
+
except self.lambda_client.exceptions.ResourceConflictException:
|
|
349
|
+
pass
|
|
350
|
+
|
|
351
|
+
if use_reserved_concurrency:
|
|
352
|
+
reserved = 10 if branch == "production" else 3
|
|
353
|
+
try:
|
|
354
|
+
self.lambda_client.put_function_concurrency(
|
|
355
|
+
FunctionName=queue_handler["FunctionName"],
|
|
356
|
+
ReservedConcurrentExecutions=reserved,
|
|
357
|
+
)
|
|
358
|
+
except Exception:
|
|
359
|
+
self.lambda_client.delete_function_concurrency(
|
|
360
|
+
FunctionName=queue_handler["FunctionName"]
|
|
361
|
+
)
|
|
362
|
+
else:
|
|
363
|
+
try:
|
|
364
|
+
self.lambda_client.delete_function_concurrency(
|
|
365
|
+
FunctionName=queue_handler["FunctionName"]
|
|
366
|
+
)
|
|
367
|
+
except Exception:
|
|
368
|
+
pass
|
|
369
|
+
|
|
370
|
+
def check_policies(
|
|
371
|
+
self,
|
|
372
|
+
):
|
|
373
|
+
# Attach a role policy
|
|
374
|
+
response = iam_client.list_attached_role_policies(
|
|
375
|
+
RoleName=function["Role"].split("/")[1]
|
|
376
|
+
)
|
|
377
|
+
has_policy_attached = False
|
|
378
|
+
for policy in response["AttachedPolicies"]:
|
|
379
|
+
if policy["PolicyName"] == "lambda-vpc-execution":
|
|
380
|
+
has_policy_attached = True
|
|
381
|
+
if not has_policy_attached:
|
|
382
|
+
account_id = self.get_account_id()
|
|
383
|
+
iam_client.attach_role_policy(
|
|
384
|
+
PolicyArn=f"arn:aws:iam::{account_id}:policy/lambda-vpc-execution",
|
|
385
|
+
RoleName=function["Role"].split("/")[1],
|
|
386
|
+
)
|
|
387
|
+
time.sleep(15)
|
|
388
|
+
has_policy_attached = False
|
|
389
|
+
for policy in response["AttachedPolicies"]:
|
|
390
|
+
if policy["PolicyName"] == "AmazonSQSFullAccess":
|
|
391
|
+
has_policy_attached = True
|
|
392
|
+
if not has_policy_attached:
|
|
393
|
+
iam_client.attach_role_policy(
|
|
394
|
+
PolicyArn="arn:aws:iam::aws:policy/AmazonSQSFullAccess",
|
|
395
|
+
RoleName=function["Role"].split("/")[1],
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
def sync(self, branch):
|
|
399
|
+
print(
|
|
400
|
+
f"\n🚀 Syncing environment variables for app '{self.app_id}' and environment '{branch}'..."
|
|
401
|
+
)
|
|
402
|
+
env_vars = self.get_merged_env_vars(branch)
|
|
403
|
+
|
|
404
|
+
print(f"\n🔧 Applying {len(env_vars)} environment variables...")
|
|
405
|
+
for function_name in self.list_lambda_functions_filtered(branch):
|
|
406
|
+
print(f"➡️ Updating Lambda function: {function_name}")
|
|
407
|
+
self.update_lambda_env(function_name, env_vars)
|
|
408
|
+
|
|
409
|
+
print(
|
|
410
|
+
"✅ Environment variables successfully applied to matching Lambda functions.\n"
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def main():
|
|
415
|
+
app_id = "d3c209q3ri53mk"
|
|
416
|
+
branch = "demo"
|
|
417
|
+
app = AmplifyProject(app_id)
|
|
418
|
+
print(app.list_backend_branches())
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
if __name__ == "__main__":
|
|
422
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: velocity-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.80
|
|
4
4
|
Summary: A rapid application development library for interfacing with data storage
|
|
5
5
|
Author-email: Paul Perez <pperez@codeclubs.org>
|
|
6
6
|
Project-URL: Homepage, https://codeclubs.org/projects/velocity
|
|
@@ -14,6 +14,7 @@ Requires-Dist: requests
|
|
|
14
14
|
Requires-Dist: jinja2
|
|
15
15
|
Requires-Dist: xlrd
|
|
16
16
|
Requires-Dist: openpyxl
|
|
17
|
+
Requires-Dist: sqlparse
|
|
17
18
|
Provides-Extra: mysql
|
|
18
19
|
Requires-Dist: mysql-connector-python; extra == "mysql"
|
|
19
20
|
Provides-Extra: sqlserver
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/aws/handlers/lambda_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/postgres/__init__.py
RENAMED
|
File without changes
|
{velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/postgres/operators.py
RENAMED
|
File without changes
|
{velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/postgres/reserved.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/sqlite_reserved.py
RENAMED
|
File without changes
|
|
File without changes
|
{velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity/db/servers/sqlserver_reserved.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{velocity_python-0.0.78 → velocity_python-0.0.80}/src/velocity_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|