vortex-cli 4.14.0__tar.gz → 4.15.0__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 (41) hide show
  1. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/PKG-INFO +1 -1
  2. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/setup.cfg +1 -1
  3. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/cli.py +36 -32
  4. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/db.py +97 -66
  5. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/main.py +1 -2
  6. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex_cli.egg-info/PKG-INFO +1 -1
  7. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/LICENSE +0 -0
  8. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/README.md +0 -0
  9. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/setup.py +0 -0
  10. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/__init__.py +0 -0
  11. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/__main__.py +0 -0
  12. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/colour.py +0 -0
  13. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/__init__.py +0 -0
  14. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/clean.py +0 -0
  15. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/clone.py +0 -0
  16. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/code.py +0 -0
  17. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/config.py +0 -0
  18. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/copy.py +0 -0
  19. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/delete.py +0 -0
  20. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/docs.py +0 -0
  21. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/execute.py +0 -0
  22. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/find.py +0 -0
  23. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/grep.py +0 -0
  24. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/list.py +0 -0
  25. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/log.py +0 -0
  26. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/new.py +0 -0
  27. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/commands/watch.py +0 -0
  28. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/constants.py +0 -0
  29. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/docs/Blackbook.pdf +0 -0
  30. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/lib/puakma.jar +0 -0
  31. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/logging.py +0 -0
  32. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/models.py +0 -0
  33. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/soap.py +0 -0
  34. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/spinner.py +0 -0
  35. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/util.py +0 -0
  36. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex/workspace.py +0 -0
  37. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex_cli.egg-info/SOURCES.txt +0 -0
  38. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex_cli.egg-info/dependency_links.txt +0 -0
  39. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex_cli.egg-info/entry_points.txt +0 -0
  40. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex_cli.egg-info/requires.txt +0 -0
  41. {vortex_cli-4.14.0 → vortex_cli-4.15.0}/vortex_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vortex_cli
3
- Version: 4.14.0
3
+ Version: 4.15.0
4
4
  Home-page: https://github.com/jordanamos/vortex-cli
5
5
  Author: Jordan Amos
6
6
  Author-email: jordan.amos@gmail.com
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = vortex_cli
3
- version = 4.14.0
3
+ version = 4.15.0
4
4
  long_description = file: README.md
5
5
  long_description_content_type = text/markdown
6
6
  url = https://github.com/jordanamos/vortex-cli
@@ -111,11 +111,14 @@ def validate_args(
111
111
 
112
112
  elif args.command == "clone" and not (args.app_ids or args.reclone):
113
113
  clone_parser.error("Please specifiy the APP_ID[s] to clone or use '--reclone'")
114
- elif args.command == "db":
115
- if args.sql and not args.conn_id:
116
- db_parser.error("The '--sql' option requires '--connection-id'")
117
- if (args.schema or args.list) and not args.db_name:
118
- db_parser.error("Options '--schema' and '--list' require '--db-name'")
114
+ elif args.command == "db" and args.sql is not None:
115
+ try:
116
+ args.database = int(args.database)
117
+ except ValueError:
118
+ db_parser.error(
119
+ "Positional argument 'database' must be a database connection "
120
+ "ID (int) when using the '--sql' option"
121
+ )
119
122
 
120
123
 
121
124
  def add_code_parser(
@@ -523,9 +526,25 @@ def add_delete_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None
523
526
 
524
527
  def add_db_parser(command_parser: _SubParsersAction[ArgumentParser]) -> ArgumentParser:
525
528
  db_parser = command_parser.add_parser("db", help="Interact with Databases")
526
-
529
+ db_parser.add_argument(
530
+ "database",
531
+ help=(
532
+ "The database name to connect to or the "
533
+ "database connection ID if using --sql"
534
+ ),
535
+ )
527
536
  db_mutex = db_parser.add_mutually_exclusive_group(required=True)
528
- db_mutex.add_argument("--sql", help="Execute a given SQL query")
537
+ db_mutex.add_argument(
538
+ "--sql",
539
+ type=str,
540
+ nargs="?",
541
+ const="",
542
+ help=(
543
+ "The SQL query to execute. "
544
+ "Requires <database> to be a database connection id (int). "
545
+ "Interactively prompts for the sql if the value is empty (Non Windows)."
546
+ ),
547
+ )
529
548
  db_mutex.add_argument(
530
549
  "--schema",
531
550
  metavar="TABLE_NAME",
@@ -537,38 +556,17 @@ def add_db_parser(command_parser: _SubParsersAction[ArgumentParser]) -> Argument
537
556
  action="store_true",
538
557
  help="List the tables in the Database",
539
558
  )
540
- list_schema_group = db_parser.add_argument_group("List/Schema Options")
541
- list_schema_group.add_argument(
542
- "--db-name",
543
- "--name",
544
- "-n",
545
- metavar="DB_NAME",
546
- dest="db_name",
547
- help="The Database Name.",
548
- )
549
559
  sql_group = db_parser.add_argument_group("SQL Options")
550
- sql_group.add_argument(
551
- "--connection-id",
552
- "--id",
553
- metavar="INT",
554
- dest="conn_id",
555
- type=int,
556
- help="The Database Connection ID to query. (Required for SQL)",
557
- )
558
- sql_group.add_argument(
559
- "--update",
560
- "-u",
561
- action="store_true",
562
- help="Set this flag when running querys for altering/updating the database",
563
- )
564
560
  sql_group.add_argument(
565
561
  "--all-cols",
562
+ "-a",
566
563
  action="store_false",
567
- help="Show all columns in the output. Default is max 8 columns",
564
+ help="Show all columns in the output. Default is max 6 columns",
568
565
  dest="truncate_cols",
569
566
  )
570
567
  sql_group.add_argument(
571
568
  "--limit",
569
+ "-n",
572
570
  metavar="INT",
573
571
  type=_check_int_in_range,
574
572
  help=(
@@ -577,7 +575,13 @@ def add_db_parser(command_parser: _SubParsersAction[ArgumentParser]) -> Argument
577
575
  "This flag adds the 'LIMIT' clause of the given SQL query "
578
576
  "and so that clause should never be used when executing a query."
579
577
  ),
580
- default=5,
578
+ default=10,
579
+ )
580
+ sql_group.add_argument(
581
+ "--update",
582
+ "-u",
583
+ action="store_true",
584
+ help="Set this flag when running querys for altering/updating the database",
581
585
  )
582
586
  return db_parser
583
587
 
@@ -1,6 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
+ import os
5
+ import subprocess
6
+ import sys
7
+ import tempfile
4
8
  from typing import Any
5
9
 
6
10
  import tabulate
@@ -9,13 +13,59 @@ from vortex.models import DatabaseConnection
9
13
  from vortex.models import PuakmaServer
10
14
  from vortex.workspace import Workspace
11
15
 
12
-
13
16
  logger = logging.getLogger("vortex")
14
17
 
18
+ GET_SCHEMA_SQL = """\
19
+ SELECT DISTINCT
20
+ CASE
21
+ WHEN isprimarykey = '1'
22
+ THEN 'PK'
23
+ ELSE ''
24
+ END AS "PK"
25
+ , attributename AS "Attribute"
26
+ , CASE WHEN ref.tableid > 0
27
+ THEN UPPER(ref.tablename)
28
+ ELSE ''
29
+ END AS "References"
30
+ , CASE WHEN type IN ('VARCHAR', 'CHAR')
31
+ THEN CONCAT(type, ' (', typesize, ')')
32
+ ELSE type
33
+ END AS "Type"
34
+ , CASE
35
+ WHEN isunique = '1'
36
+ THEN 'Yes'
37
+ ELSE 'No'
38
+ END AS "Unique"
39
+ , CASE
40
+ WHEN allownull = '1'
41
+ THEN 'Yes'
42
+ ELSE 'No'
43
+ END AS "Allow Null"
44
+ FROM attribute a
45
+ INNER JOIN pmatable t
46
+ ON t.tableid = a.tableid
47
+ INNER JOIN dbconnection db
48
+ ON db.dbconnectionid = t.dbconnectionid
49
+ LEFT JOIN pmatable ref
50
+ ON ref.tableid::VARCHAR = a.reftable
51
+ WHERE LOWER(db.dbname) = '%s'
52
+ AND LOWER(t.tablename) = '%s'
53
+ ORDER BY attributename
54
+ """
55
+
56
+ LIST_TABLES_SQL = """\
57
+ SELECT DISTINCT tablename
58
+ , description
59
+ FROM dbconnection db
60
+ INNER JOIN pmatable t
61
+ ON db.dbconnectionid = t.dbconnectionid
62
+ WHERE LOWER(db.dbname)='%s'
63
+ """
64
+
15
65
 
16
66
  def _truncate_cols(result: tuple[dict[str, Any], ...]) -> tuple[dict[str, Any], ...]:
17
67
  number_of_cols = len(result[0])
18
- max_cols = 8
68
+ max_cols = 6
19
69
  if number_of_cols <= max_cols:
20
70
  return result
21
71
 
@@ -40,12 +90,18 @@ def _output_result(
40
90
  truncate_cols: bool = False,
41
91
  db_name: str | None = None,
42
92
  schema_table: str | None = None,
93
+ list_tables: bool = False,
43
94
  ) -> int:
44
95
  with server:
96
+ logger.debug("Executing query")
97
+ logger.debug(sql)
45
98
  result = tuple(server.database_designer.execute_query(conn_id, sql, update))
46
99
  if schema_table and not result:
47
100
  logger.error(f"Table '{schema_table}' not found in database '{db_name}'")
48
101
  return 1
102
+ if list_tables and not result:
103
+ logger.error(f"No tables found in database '{db_name}'.")
104
+ return 1
49
105
  if result:
50
106
  if truncate_cols:
51
107
  result = _truncate_cols(result)
@@ -72,11 +128,25 @@ def _is_connection_cloned(workspace: Workspace, conn_id: int) -> bool:
72
128
  return any(conn.id == conn_id for conn in conns)
73
129
 
74
130
 
131
+ def _read_from_editor() -> str:
132
+ fd, tmp_sql_file = tempfile.mkstemp(suffix=".sql")
133
+ os.close(fd)
134
+ try:
135
+ subprocess.call(["nano", tmp_sql_file])
136
+ with open(tmp_sql_file) as f:
137
+ sql_query = f.read().strip()
138
+ except Exception:
139
+ os.remove(tmp_sql_file)
140
+ raise
141
+ else:
142
+ os.remove(tmp_sql_file)
143
+ return sql_query
144
+
145
+
75
146
  def db(
76
147
  workspace: Workspace,
77
148
  server: PuakmaServer,
78
- conn_id: int | None,
79
- db_name: str | None,
149
+ database: int | str,
80
150
  sql: str | None,
81
151
  update: bool = False,
82
152
  limit_n_results: int = 5,
@@ -84,74 +154,35 @@ def db(
84
154
  list_tables: bool = False,
85
155
  truncate_cols: bool = False,
86
156
  ) -> int:
87
- do_schema = schema_table is not None
88
- db_name = db_name or ""
89
- conn_id = conn_id or -1
90
- if do_schema:
91
- sql = f"""\
92
- SELECT DISTINCT
93
- CASE
94
- WHEN isprimarykey = '1'
95
- THEN 'PK'
96
- ELSE ''
97
- END AS "PK"
98
- , attributename AS "Attribute"
99
- , CASE WHEN ref.tableid > 0
100
- THEN UPPER(ref.tablename)
101
- ELSE ''
102
- END AS "References"
103
- , CASE WHEN type IN ('VARCHAR', 'CHAR')
104
- THEN CONCAT(type, ' (', typesize, ')')
105
- ELSE type
106
- END AS "Type"
107
- , CASE
108
- WHEN isunique = '1'
109
- THEN 'Yes'
110
- ELSE 'No'
111
- END AS "Unique"
112
- , CASE
113
- WHEN allownull = '1'
114
- THEN 'Yes'
115
- ELSE 'No'
116
- END AS "Allow Null"
117
- FROM attribute a
118
- INNER JOIN pmatable t
119
- ON t.tableid = a.tableid
120
- INNER JOIN dbconnection db
121
- ON db.dbconnectionid = t.dbconnectionid
122
- LEFT JOIN pmatable ref
123
- ON ref.tableid::VARCHAR = a.reftable
124
- WHERE LOWER(db.dbname) = '{db_name.lower()}'
125
- AND LOWER(t.tablename) = '{schema_table}'
126
- ORDER BY attributename
127
- """
128
- update = False
129
- truncate_cols = False
130
- conn_id = server.puakma_db_conn_id
131
- elif list_tables:
132
- sql = f"""\
133
- SELECT DISTINCT tablename
134
- , description
135
- FROM dbconnection db
136
- INNER JOIN pmatable t
137
- ON db.dbconnectionid = t.dbconnectionid
138
- WHERE LOWER(db.dbname)='{db_name.lower()}'
139
- """
140
- update = False
141
- truncate_cols = False
142
- conn_id = server.puakma_db_conn_id
143
- elif sql is not None:
157
+ if sql is not None:
158
+ if sql.strip() == "":
159
+ if sys.platform == "win32":
160
+ sql = None
161
+ else:
162
+ sql = _read_from_editor()
163
+ db_name = None
164
+ conn_id = int(database)
144
165
  # If we're modifying a database, lets check that its locally
145
166
  # cloned to ensure intent
146
- if not _is_connection_cloned(workspace, conn_id):
167
+ if update and not _is_connection_cloned(workspace, conn_id):
147
168
  logger.error(f"No cloned applications with DB Connection ID '{conn_id}'")
148
169
  return 1
149
- if not update:
170
+ if sql and not update:
150
171
  sql = sql + f" LIMIT {limit_n_results}"
172
+ else:
173
+ db_name = str(database).lower()
174
+ update = False
175
+ truncate_cols = False
176
+ conn_id = server.puakma_db_conn_id
177
+ if schema_table is not None:
178
+ sql = GET_SCHEMA_SQL % (db_name, schema_table)
179
+ elif list_tables:
180
+ sql = LIST_TABLES_SQL % db_name
151
181
 
152
- if sql is None:
153
- raise ValueError("No Query to execute.")
182
+ if not sql:
183
+ logger.error("No Query to execute.")
184
+ return 1
154
185
 
155
186
  return _output_result(
156
- server, conn_id, sql, update, truncate_cols, db_name, schema_table
187
+ server, conn_id, sql, update, truncate_cols, db_name, schema_table, list_tables
157
188
  )
@@ -230,8 +230,7 @@ def main(argv: Sequence[str] | None = None) -> int:
230
230
  return db(
231
231
  workspace,
232
232
  server,
233
- args.conn_id,
234
- args.db_name,
233
+ args.database,
235
234
  args.sql,
236
235
  update=args.update,
237
236
  limit_n_results=args.limit,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vortex_cli
3
- Version: 4.14.0
3
+ Version: 4.15.0
4
4
  Home-page: https://github.com/jordanamos/vortex-cli
5
5
  Author: Jordan Amos
6
6
  Author-email: jordan.amos@gmail.com
File without changes
File without changes
File without changes
File without changes
File without changes