pyegeria 5.4.0.3__py3-none-any.whl → 5.4.0.4__py3-none-any.whl

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.
@@ -0,0 +1,411 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ SPDX-License-Identifier: Apache-2.0
4
+ Copyright Contributors to the ODPi Egeria project.
5
+
6
+ A command that takes an output format set name, looks up the format set using select_output_format_set,
7
+ and invokes the specified function with parameters filled from command line arguments.
8
+
9
+ This command works with any format set defined in output_formatter.py that has an "action" property.
10
+ It dynamically determines the appropriate client class and method to call based on the format set,
11
+ and prompts for any required parameters that aren't provided on the command line.
12
+
13
+ Features:
14
+ - Works with any format set that has an "action" property
15
+ - Dynamically adds command-line arguments based on the format set's user_params
16
+ - Prompts for required parameters if not provided
17
+ - Supports multiple output formats (TABLE, DICT, FORM, REPORT, HTML, LIST)
18
+ - Handles both instance methods and standalone functions
19
+ - Provides informative error messages
20
+
21
+ Examples:
22
+ # List all collections using the Collections format set
23
+ format_set_action --format-set Collections --search-string "*" --output-format TABLE
24
+
25
+ # Output collections in DICT format
26
+ format_set_action --format-set Collections --search-string "*" --output-format DICT
27
+
28
+ # Use a specific search string
29
+ format_set_action --format-set Collections --search-string "Data*" --output-format TABLE
30
+
31
+ # Use a different format set (Data Dictionary)
32
+ format_set_action --format-set "Data Dictionary" --search-string "*" --output-format TABLE
33
+
34
+ # Use a format set with multiple parameters (will prompt for missing parameters)
35
+ format_set_action --format-set DigitalProducts --output-format REPORT
36
+ """
37
+ import argparse
38
+ import json
39
+ import os
40
+ import time
41
+ from typing import Any, Dict, List
42
+ from loguru import logger
43
+ from rich import box
44
+ from rich.console import Console
45
+ from rich.markdown import Markdown
46
+ from rich.prompt import Prompt
47
+ from rich.table import Table
48
+ from rich.text import Text
49
+
50
+ from pyegeria import (
51
+ EgeriaTech,
52
+ NO_ELEMENTS_FOUND,
53
+ config_logging,
54
+ get_app_config
55
+ )
56
+ from pyegeria._output_formats import select_output_format_set, get_output_format_set_heading, \
57
+ get_output_format_set_description
58
+ from pyegeria._exceptions_new import PyegeriaException, print_exception_response
59
+ from pyegeria.egeria_tech_client import EgeriaTech
60
+
61
+ EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
62
+ EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
63
+ app_settings = get_app_config()
64
+ app_config = app_settings["Environment"]
65
+ config_logging()
66
+
67
+ console = Console(
68
+ style="bold bright_white on black",
69
+ width=app_config["Console Width"],
70
+ force_terminal=not app_config["Egeria Jupyter"],
71
+ )
72
+
73
+ def execute_format_set_action(
74
+ format_set_name: str,
75
+ view_server: str = app_config['Egeria View Server'],
76
+ view_url: str = app_config['Egeria View Server URL'],
77
+ user: str = EGERIA_USER,
78
+ user_pass: str = EGERIA_USER_PASSWORD,
79
+ output_format: str = "TABLE",
80
+ **kwargs
81
+ ):
82
+ """
83
+ Looks up the specified output format set, extracts the function and parameters,
84
+ and invokes the function with the parameters.
85
+
86
+ Parameters
87
+ ----------
88
+ format_set_name : str
89
+ The name of the output format set to use.
90
+ view_server : str
91
+ The view server name or address where the Egeria services are hosted.
92
+ view_url : str
93
+ The URL of the platform the view server is on.
94
+ user : str
95
+ The user ID for authentication with the Egeria server.
96
+ user_pass : str
97
+ The password for authentication with the Egeria server.
98
+ jupyter : bool, optional
99
+ A boolean indicating whether the output is intended for a Jupyter notebook.
100
+ width : int, optional
101
+ The width of the console output.
102
+ output_format : str, optional
103
+ Format of the output. Default is TABLE.
104
+ **kwargs : dict
105
+ Additional parameters to override the default parameters in the format set.
106
+ """
107
+ # Get the output format set
108
+ # logger.info(f"Entering execute_format_set_action, format set name: {format_set_name}, search_string: {kwargs.get('search_string',"meow")}")
109
+ # logger.info(json.dumps(kwargs, indent=2))
110
+ format_set = select_output_format_set(format_set_name, output_format)
111
+ if not format_set:
112
+ print(f"Error: Output format set '{format_set_name}' does not have a format compatible with output format '{output_format}'.")
113
+ return
114
+
115
+ # Check if the format set has an action property
116
+ if "action" not in format_set:
117
+ print(f"Error: Output format set '{format_set_name}' does not have an action property.")
118
+ return
119
+
120
+ # Extract the function and parameters from the action property
121
+ action = format_set["action"][0] # Assuming there's only one action
122
+ func = action.get("function")
123
+ user_params = action.get("user_params", [])
124
+ spec_params = action.get("spec_params", {})
125
+
126
+ # Create a params dictionary from user_params and spec_params
127
+ params = {}
128
+ for param in user_params:
129
+ if param in kwargs and kwargs[param]:
130
+ params[param] = kwargs[param]
131
+ elif param not in kwargs and param not in spec_params:
132
+ print(f"Warning: Required parameter '{param}' not provided for format set '{format_set_name}'.")
133
+
134
+ # Add spec_params to params
135
+ params.update(spec_params)
136
+
137
+ params['output_format'] = output_format
138
+ params['output_format_set'] = format_set_name
139
+
140
+ # Determine the appropriate client class based on the format set name or function
141
+ client_class = None
142
+ method_name = None
143
+
144
+ # If function name is provided as a string, parse it to get class and method
145
+ if isinstance(func, str) and "." in func:
146
+ class_name, method_name = func.split(".")
147
+ # if class_name == "CollectionManager":
148
+ # client_class = CollectionManager
149
+ # elif class_name == "EgeriaTech":
150
+ client_class = EgeriaTech
151
+ # Add more client classes as needed
152
+
153
+ # # If client_class is still None, determine based on format set name
154
+ # if client_class is None:
155
+ # if format_set_name in ["Collections", "Data Dictionary", "Data Specification", "DigitalProducts"]:
156
+ # client_class = CollectionManager
157
+ # method_name = "find_collections"
158
+ # else:
159
+ # # Default to CollectionManager for now
160
+ # client_class = CollectionManager
161
+
162
+ # Create the client instance
163
+ client = client_class(view_server, view_url, user_id=user, user_pwd=user_pass)
164
+
165
+ # If method_name is set, get the method from the client
166
+ # Note: We need to convert func from string to method reference even if func is already set
167
+ if method_name:
168
+ if hasattr(client, method_name):
169
+ func = getattr(client, method_name)
170
+ else:
171
+ print(f"Error: Method '{method_name}' not found in client class '{client_class.__name__}'.")
172
+ return
173
+
174
+ # Check if we have a valid function to call
175
+ if not func and not method_name:
176
+ print(f"Error: No valid function found for format set '{format_set_name}'.")
177
+ return
178
+
179
+ client.create_egeria_bearer_token()
180
+
181
+ # Get heading and description information
182
+ heading = get_output_format_set_heading(format_set_name)
183
+ desc = get_output_format_set_description(format_set_name)
184
+ preamble = f"# {heading}\n{desc}\n\n" if heading and desc else ""
185
+
186
+ try:
187
+ # Invoke the function with the parameters
188
+ if output_format != "TABLE":
189
+ file_path = os.path.join(app_config['Pyegeria Root'], app_config['Dr.Egeria Outbox'])
190
+ if output_format == "HTML":
191
+ file_name = f"{format_set_name}-{time.strftime('%Y-%m-%d-%H-%M-%S')}.html"
192
+ elif output_format == "JSON":
193
+ file_name = f"{format_set_name}-{time.strftime('%Y-%m-%d-%H-%M-%S')}.json"
194
+ elif output_format == "DICT":
195
+ file_name = f"{format_set_name}-{time.strftime('%Y-%m-%d-%H-%M-%S')}.py"
196
+ else:
197
+ file_name = f"{format_set_name}-{time.strftime('%Y-%m-%d-%H-%M-%S')}.md"
198
+ full_file_path = os.path.join(file_path, file_name)
199
+ os.makedirs(os.path.dirname(full_file_path), exist_ok=True)
200
+
201
+ # Add output_format to params
202
+ params['output_format'] = output_format
203
+
204
+ # Call the function with the parameters
205
+ try:
206
+ if isinstance(func, type(client.find_collections)): # It's a method of the client
207
+ # Call the function as an instance method of the client
208
+ output = func(**params)
209
+ else:
210
+ # For standalone functions, call with client as first argument
211
+ output = func(client, **params)
212
+ except TypeError as e:
213
+ # Handle parameter mismatch errors
214
+ print(f"Error calling function: {e}")
215
+ print(f"Parameters provided: {params}")
216
+ return
217
+
218
+ if output == NO_ELEMENTS_FOUND:
219
+ print(f"\n==> No elements found for format set '{format_set_name}'")
220
+ return
221
+ elif isinstance(output, (str, list)) and output_format == "DICT":
222
+ output = json.dumps(output, indent=4)
223
+ elif isinstance(output, (str, list)) and output_format == "REPORT":
224
+ output = preamble + output
225
+
226
+ with open(full_file_path, 'w') as f:
227
+ f.write(output)
228
+ print(f"\n==> Output written to {full_file_path}")
229
+ return
230
+
231
+ # For TABLE output, add output_format to params
232
+ params['output_format'] = "DICT"
233
+
234
+ # Call the function and create a table
235
+ try:
236
+ if isinstance(func, type(client.find_collections)): # It's a method of the client
237
+ # Call the function as an instance method of the client
238
+ result = func(**params)
239
+ else:
240
+ # For standalone functions, call with client as first argument
241
+ result = func(client, **params)
242
+ except TypeError as e:
243
+ # Handle parameter mismatch errors
244
+ print(f"Error calling function: {e}")
245
+ print(f"Parameters provided: {params}")
246
+ return
247
+
248
+ if not result or result == NO_ELEMENTS_FOUND:
249
+ print(f"\n==> No elements found for format set '{format_set_name}'")
250
+ return
251
+
252
+
253
+ if heading and desc:
254
+ console.print(Markdown(preamble))
255
+
256
+ # Create a table for the results
257
+ table = Table(
258
+ title=f"{format_set_name} @ {time.asctime()}",
259
+ style="bright_white on black",
260
+ header_style="bright_white on dark_blue",
261
+ title_style="bold white on black",
262
+ caption_style="white on black",
263
+ show_lines=True,
264
+ box=box.ROUNDED,
265
+ caption=f"View Server '{view_server}' @ Platform - {view_url}",
266
+ expand=True,
267
+ )
268
+
269
+ # Handle both list and dictionary results
270
+ if isinstance(result, list):
271
+ if not result:
272
+ print(f"\n==> No elements found for format set '{format_set_name}'")
273
+ return
274
+
275
+ # Sort the results by display_name if available
276
+ if "display_name" in result[0]:
277
+ sorted_results = sorted(result, key=lambda k: k.get("display_name", ""))
278
+ elif "Display Name" in result[0]:
279
+ sorted_results = sorted(result, key=lambda k: k.get("Display Name", ""))
280
+ else:
281
+ sorted_results = result
282
+
283
+ # Add columns dynamically based on the first result
284
+ column_headings = list(sorted_results[0].keys())
285
+ for heading in column_headings:
286
+ table.add_column(heading, justify="left", style="cyan")
287
+
288
+ # Add rows
289
+ for item in sorted_results:
290
+ row_values = []
291
+ for key in column_headings:
292
+ value = item.get(key, "")
293
+ row_values.append(str(value))
294
+
295
+ table.add_row(*row_values)
296
+ else:
297
+ # Handle single dictionary result
298
+ column_headings = list(result.keys())
299
+ for heading in column_headings:
300
+ table.add_column(heading, justify="left", style="cyan")
301
+
302
+ row_values = []
303
+ for key in column_headings:
304
+ value = result.get(key, "")
305
+ row_values.append(str(value))
306
+
307
+ table.add_row(*row_values)
308
+
309
+ # Print the table
310
+
311
+ console.print(table)
312
+
313
+ except PyegeriaException as e:
314
+ print_exception_response(e)
315
+ finally:
316
+ client.close_session()
317
+
318
+
319
+ def main():
320
+ # First, parse just the format-set argument to determine which other arguments to add
321
+ initial_parser = argparse.ArgumentParser(add_help=False)
322
+ initial_parser.add_argument("--format-set", help="Name of the output format set", required=True)
323
+ initial_args, _ = initial_parser.parse_known_args()
324
+
325
+ # Get the format set to determine user_params
326
+ format_set_name = initial_args.format_set
327
+ format_set = select_output_format_set(format_set_name, "ANY")
328
+
329
+ # Check if the format set exists
330
+ if not format_set:
331
+ print(f"Error: Format set for '{format_set_name}' not found.")
332
+ print("Available format sets:")
333
+ from pyegeria._output_formats import output_format_set_list
334
+ for name in output_format_set_list():
335
+ print(f" - {name}")
336
+ return
337
+
338
+ # Create the full parser with all arguments
339
+ parser = argparse.ArgumentParser(description="Execute an action from an output format set")
340
+ parser.add_argument("--format-set", help="Name of the output format set", required=True)
341
+ parser.add_argument("--server", help="Name of the server to connect to")
342
+ parser.add_argument("--url", help="URL Platform to connect to")
343
+ parser.add_argument("--userid", help="User Id")
344
+ parser.add_argument("--password", help="User Password")
345
+ parser.add_argument("--output-format", help="Output format (TABLE, DICT, FORM, REPORT, HTML, LIST)",
346
+ choices=["TABLE", "DICT", "FORM", "REPORT", "HTML", "LIST"], default="TABLE")
347
+
348
+
349
+
350
+ # Add arguments based on the format set's user_params
351
+ user_params = []
352
+ if "action" in format_set:
353
+ action = format_set["action"][0] # Assuming there's only one action
354
+ user_params = action.get("user_params", [])
355
+
356
+ for param in user_params:
357
+ parser.add_argument(f"--{param.replace('_', '-')}", help=f"{param.replace('_', ' ')} parameter")
358
+ else:
359
+ print(f"Error: Format set '{format_set_name}' does not have an action property.")
360
+ return
361
+
362
+ args = parser.parse_args()
363
+ app_settings = get_app_config()
364
+ app_config = app_settings["Environment"]
365
+
366
+ server = args.server if args.server is not None else app_config['Egeria View Server']
367
+ url = args.url if args.url is not None else app_config['Egeria View Server URL']
368
+ userid = args.userid if args.userid is not None else EGERIA_USER
369
+ user_pass = args.password if args.password is not None else EGERIA_USER_PASSWORD
370
+ format_set_name = args.format_set
371
+ output_format = args.output_format
372
+
373
+ # Collect all user parameters from args
374
+ kwargs = {}
375
+ for param in user_params:
376
+ arg_name = param.replace('_', '-')
377
+ if hasattr(args, arg_name) and getattr(args, arg_name) is not None:
378
+ kwargs[param] = getattr(args, arg_name)
379
+ elif hasattr(args, param) and getattr(args, param) is not None:
380
+ kwargs[param] = getattr(args, param)
381
+
382
+ try:
383
+ # If a required parameter is not provided, prompt for it
384
+ for param in user_params:
385
+ if param not in kwargs:
386
+ default_value = "*" if param == "search_string" else ""
387
+ prompt_text = f"Enter {param.replace('_', ' ')}:"
388
+ if default_value:
389
+ prompt_text += f" (default: {default_value})"
390
+ value = Prompt.ask(prompt_text, default=default_value).strip()
391
+ kwargs[param] = value
392
+ print(f"Using format set {format_set_name} and output format {output_format} with parameters: {kwargs} ")
393
+ execute_format_set_action(
394
+ format_set_name=format_set_name,
395
+ view_server=server,
396
+ view_url=url,
397
+ user=userid,
398
+ user_pass=user_pass,
399
+ output_format=output_format,
400
+ **kwargs
401
+ )
402
+
403
+ except KeyboardInterrupt:
404
+ pass
405
+
406
+ except PyegeriaException as e:
407
+ print_exception_response(e)
408
+
409
+
410
+ if __name__ == "__main__":
411
+ main()
commands/cli/debug_log ADDED
File without changes
commands/cli/egeria.py CHANGED
@@ -14,7 +14,11 @@ import sys
14
14
 
15
15
  import click
16
16
  from trogon import tui
17
+ from loguru import logger
17
18
 
19
+ from pyegeria import config_logging, get_app_config
20
+
21
+ from commands.cat.list_format_set import execute_format_set_action
18
22
  from commands.cat.get_asset_graph import asset_viewer
19
23
  from commands.cat.get_collection import collection_viewer
20
24
  from commands.cat.get_project_dependencies import project_dependency_viewer
@@ -131,68 +135,66 @@ from commands.tech.list_tech_templates import display_templates_spec
131
135
  from commands.tech.list_valid_metadata_values import display_metadata_values
132
136
  from commands.tech.generic_actions import delete_element
133
137
 
138
+ EGERIA_USER = os.environ.get("EGERIA_USER", "erinoverview")
139
+ EGERIA_USER_PASSWORD = os.environ.get("EGERIA_USER_PASSWORD", "secret")
140
+ app_settings = get_app_config()
141
+ app_config = app_settings["Environment"]
142
+ # config_logging()
143
+
144
+
134
145
  @tui()
135
146
  # @tui('menu', 'menu', 'A textual object_action line interface')
136
- @click.version_option("0.5.2 ", prog_name="hey_egeria")
147
+ @click.version_option("5.4 ", prog_name="hey_egeria")
137
148
  @click.group()
138
149
  @click.option(
139
150
  "--server",
140
- default=os.environ.get("EGERIA_METADATA_STORE", "active-metadata-store"),
151
+ default=app_config['Egeria Metadata Store'],
141
152
  help="Egeria metadata store to work with",
142
153
  )
143
154
  @click.option(
144
155
  "--url",
145
- default=os.environ.get("EGERIA_PLATFORM_URL", "https://localhost:9443"),
156
+ default=app_config["Egeria Platform URL"],
146
157
  help="URL of Egeria metadata store platform to connect to",
147
158
  )
148
159
  @click.option(
149
160
  "--integration_daemon",
150
- default=os.environ.get("EGERIA_INTEGRATION_DAEMON", "qs-integration-daemon"),
161
+ default=app_config["Egeria Integration Daemon"],
151
162
  help="Egeria integration daemon to work with",
152
163
  )
153
164
  @click.option(
154
165
  "--integration_daemon_url",
155
- default=os.environ.get("EGERIA_INTEGRATION_DAEMON_URL", "https://localhost:9443"),
166
+ default=app_config['Egeria Integration Daemon URL'],
156
167
  help="URL of Egeria integration daemon platform to connect to",
157
168
  )
158
169
  @click.option(
159
170
  "--view_server",
160
- default=os.environ.get("EGERIA_VIEW_SERVER", "qs-view-server"),
171
+ default=app_config['Egeria View Server'],
161
172
  help="Egeria view server to work with",
162
173
  )
163
174
  @click.option(
164
175
  "--view_server_url",
165
- default=os.environ.get("EGERIA_VIEW_SERVER_URL", "https://localhost:9443"),
176
+ default=app_config['Egeria View Server URL'],
166
177
  help="URL of Egeria view server platform to connect to",
167
178
  )
168
179
  @click.option(
169
180
  "--engine_host",
170
- default=os.environ.get("EGERIA_ENGINE_HOST", "qs-engine-host"),
181
+ default=app_config['Egeria Engine Host'],
171
182
  help="Egeria engine host to work with",
172
183
  )
173
184
  @click.option(
174
185
  "--engine_host_url",
175
- default=os.environ.get("EGERIA_ENGINE_HOST_URL", "https://localhost:9443"),
186
+ default=app_config['Egeria Engine Host URL'],
176
187
  help="URL of Egeria engine host platform to connect to",
177
188
  )
178
- @click.option(
179
- "--admin_user",
180
- default=os.environ.get("EGERIA_ADMIN_USER", "garygeeke"),
181
- help="Egeria admin user",
182
- )
183
- @click.option(
184
- "--admin_user_password",
185
- default=os.environ.get("EGERIA_ADMIN_PASSWORD", "secret"),
186
- help="Egeria admin password",
187
- )
189
+
188
190
  @click.option(
189
191
  "--userid",
190
- default=os.environ.get("EGERIA_USER", "erinoverview"),
192
+ default=EGERIA_USER,
191
193
  help="Egeria user",
192
194
  )
193
195
  @click.option(
194
196
  "--password",
195
- default=os.environ.get("EGERIA_USER_PASSWORD", "secret"),
197
+ default=EGERIA_USER_PASSWORD,
196
198
  help="Egeria user password",
197
199
  )
198
200
  @click.option("--timeout", default=60, help="Number of seconds to wait")
@@ -200,40 +202,40 @@ from commands.tech.generic_actions import delete_element
200
202
  "--jupyter",
201
203
  is_flag=True,
202
204
  type=bool,
203
- default=os.environ.get("EGERIA_JUPYTER", False),
205
+ default=app_config['Egeria Jupyter'],
204
206
  help="Enable for rendering in a Jupyter terminal",
205
207
  )
206
208
  @click.option(
207
209
  "--width",
208
- default=os.environ.get("EGERIA_WIDTH", 200),
210
+ default=app_config['Console Width'],
209
211
  type=int,
210
212
  help="Screen width, in characters, to use",
211
213
  )
212
214
  @click.option(
213
215
  "--home_glossary_guid",
214
- default=os.environ.get("EGERIA_HOME_GLOSSARY_GUID", None),
215
- help="Glossary guid to use as the home glossary",
216
+ default=app_settings['User Profile']['Egeria Home Glossary Name'],
217
+ help="Glossary name to use as the home glossary",
216
218
  )
217
219
  @click.option(
218
220
  "--glossary_path",
219
- default=os.environ.get("EGERIA_GLOSSARY_PATH", "/home/jovyan/loading-bay/glossary"),
221
+ default=app_config['Egeria Glossary Path'],
220
222
  help="Path to glossary import/export files",
221
223
  )
222
224
 
223
225
  @click.option(
224
226
  "--root_path",
225
- default=os.environ.get("EGERIA_ROOT_PATH", "/home/jovyan"),
227
+ default=app_config['Pyegeria Root'],
226
228
  help="Root path to use for file operations",
227
229
  )
228
230
 
229
231
  @click.option(
230
232
  "--inbox_path",
231
- default=os.environ.get("EGERIA_INBOX_PATH", "loading-bay/dr_egeria_inbox"),
232
- help="Path to outbox files",
233
+ default=app_config['Dr.Egeria Inbox'],
234
+ help="Path to inbox files",
233
235
  )
234
236
  @click.option(
235
237
  "--outbox_path",
236
- default=os.environ.get("EGERIA_OUTBOX_PATH", "distribution-hub/dr_egeria_outbox"),
238
+ default=app_config['Dr.Egeria Outbox'],
237
239
  help="Path to outbox files",
238
240
  )
239
241
 
@@ -248,8 +250,6 @@ def cli(
248
250
  integration_daemon_url,
249
251
  engine_host,
250
252
  engine_host_url,
251
- admin_user,
252
- admin_user_password,
253
253
  userid,
254
254
  password,
255
255
  timeout,
@@ -271,8 +271,6 @@ def cli(
271
271
  integration_daemon_url,
272
272
  engine_host,
273
273
  engine_host_url,
274
- admin_user,
275
- admin_user_password,
276
274
  userid,
277
275
  password,
278
276
  timeout,
@@ -1043,6 +1041,20 @@ def show_cat_info(ctx):
1043
1041
  """Group of md_commands to show information about various Egeria objects"""
1044
1042
  pass
1045
1043
 
1044
+ @show_cat_info.command("list-output-set")
1045
+ @click.option("--format-set", help="Format set to output")
1046
+ @click.option("--output-format", default = "TABLE", help="Output format type")
1047
+ @click.option('--search-string', default="*", help="Search string")
1048
+ @click.pass_context
1049
+ def show_format_set(ctx, format_set, output_format, search_string):
1050
+ """Dynamically generate output based on a format set"""
1051
+ c = ctx.obj
1052
+
1053
+ execute_format_set_action(
1054
+ format_set, c.view_server, c.view_server_url,
1055
+ c.userid, c.password, output_format, search_string = search_string
1056
+ )
1057
+
1046
1058
 
1047
1059
  @show_cat_info.command("tech-types")
1048
1060
  @click.option("--tech_type", default="*", help="Tech type to search for")