catocli 1.0.4__py3-none-any.whl → 1.0.5__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.

Potentially problematic release.


This version of catocli might be problematic. Click here for more details.

Files changed (92) hide show
  1. build/lib/catocli/Utils/clidriver.py +103 -0
  2. build/lib/catocli/__init__.py +2 -0
  3. build/lib/catocli/__main__.py +12 -0
  4. build/lib/catocli/parsers/custom/__init__.py +42 -0
  5. build/lib/catocli/parsers/custom/customLib.py +71 -0
  6. build/lib/catocli/parsers/mutation_admin/__init__.py +51 -0
  7. build/lib/catocli/parsers/mutation_container/__init__.py +23 -0
  8. build/lib/catocli/parsers/mutation_policy/__init__.py +357 -0
  9. build/lib/catocli/parsers/mutation_site/__init__.py +219 -0
  10. build/lib/catocli/parsers/mutation_sites/__init__.py +219 -0
  11. build/lib/catocli/parsers/parserApiClient.py +312 -0
  12. build/lib/catocli/parsers/query_accountBySubdomain/__init__.py +17 -0
  13. build/lib/catocli/parsers/query_accountMetrics/__init__.py +17 -0
  14. build/lib/catocli/parsers/query_accountRoles/__init__.py +17 -0
  15. build/lib/catocli/parsers/query_accountSnapshot/__init__.py +17 -0
  16. build/lib/catocli/parsers/query_admin/__init__.py +17 -0
  17. build/lib/catocli/parsers/query_admins/__init__.py +17 -0
  18. build/lib/catocli/parsers/query_appStats/__init__.py +17 -0
  19. build/lib/catocli/parsers/query_appStatsTimeSeries/__init__.py +17 -0
  20. build/lib/catocli/parsers/query_auditFeed/__init__.py +17 -0
  21. build/lib/catocli/parsers/query_container/__init__.py +17 -0
  22. build/lib/catocli/parsers/query_entityLookup/__init__.py +17 -0
  23. build/lib/catocli/parsers/query_events/__init__.py +17 -0
  24. build/lib/catocli/parsers/query_eventsFeed/__init__.py +17 -0
  25. build/lib/catocli/parsers/query_eventsTimeSeries/__init__.py +17 -0
  26. build/lib/catocli/parsers/query_hardwareManagement/__init__.py +17 -0
  27. build/lib/catocli/parsers/query_licensing/__init__.py +17 -0
  28. build/lib/catocli/parsers/query_policy/__init__.py +17 -0
  29. build/lib/catocli/parsers/query_siteLocation/__init__.py +17 -0
  30. build/lib/catocli/parsers/query_subDomains/__init__.py +17 -0
  31. build/lib/catocli/parsers/query_xdr/__init__.py +37 -0
  32. build/lib/catocli/parsers/raw/__init__.py +9 -0
  33. build/lib/graphql_client/__init__.py +11 -0
  34. build/lib/graphql_client/api/__init__.py +3 -0
  35. build/lib/graphql_client/api/call_api.py +73 -0
  36. build/lib/graphql_client/api_client.py +192 -0
  37. build/lib/graphql_client/api_client_types.py +404 -0
  38. build/lib/graphql_client/configuration.py +230 -0
  39. build/lib/graphql_client/models/__init__.py +13 -0
  40. build/lib/graphql_client/models/no_schema.py +71 -0
  41. build/lib/schema/catolib.py +1002 -0
  42. build/lib/schema/importSchema.py +60 -0
  43. build/lib/vendor/certifi/__init__.py +4 -0
  44. build/lib/vendor/certifi/__main__.py +12 -0
  45. build/lib/vendor/certifi/core.py +114 -0
  46. build/lib/vendor/certifi/py.typed +0 -0
  47. build/lib/vendor/six.py +998 -0
  48. build/lib/vendor/urllib3/__init__.py +211 -0
  49. build/lib/vendor/urllib3/_base_connection.py +172 -0
  50. build/lib/vendor/urllib3/_collections.py +483 -0
  51. build/lib/vendor/urllib3/_request_methods.py +278 -0
  52. build/lib/vendor/urllib3/_version.py +16 -0
  53. build/lib/vendor/urllib3/connection.py +1033 -0
  54. build/lib/vendor/urllib3/connectionpool.py +1182 -0
  55. build/lib/vendor/urllib3/contrib/__init__.py +0 -0
  56. build/lib/vendor/urllib3/contrib/emscripten/__init__.py +18 -0
  57. build/lib/vendor/urllib3/contrib/emscripten/connection.py +254 -0
  58. build/lib/vendor/urllib3/contrib/emscripten/fetch.py +418 -0
  59. build/lib/vendor/urllib3/contrib/emscripten/request.py +22 -0
  60. build/lib/vendor/urllib3/contrib/emscripten/response.py +285 -0
  61. build/lib/vendor/urllib3/contrib/pyopenssl.py +552 -0
  62. build/lib/vendor/urllib3/contrib/socks.py +228 -0
  63. build/lib/vendor/urllib3/exceptions.py +321 -0
  64. build/lib/vendor/urllib3/fields.py +341 -0
  65. build/lib/vendor/urllib3/filepost.py +89 -0
  66. build/lib/vendor/urllib3/http2/__init__.py +53 -0
  67. build/lib/vendor/urllib3/http2/connection.py +356 -0
  68. build/lib/vendor/urllib3/http2/probe.py +87 -0
  69. build/lib/vendor/urllib3/poolmanager.py +637 -0
  70. build/lib/vendor/urllib3/py.typed +2 -0
  71. build/lib/vendor/urllib3/response.py +1265 -0
  72. build/lib/vendor/urllib3/util/__init__.py +42 -0
  73. build/lib/vendor/urllib3/util/connection.py +137 -0
  74. build/lib/vendor/urllib3/util/proxy.py +43 -0
  75. build/lib/vendor/urllib3/util/request.py +256 -0
  76. build/lib/vendor/urllib3/util/response.py +101 -0
  77. build/lib/vendor/urllib3/util/retry.py +533 -0
  78. build/lib/vendor/urllib3/util/ssl_.py +513 -0
  79. build/lib/vendor/urllib3/util/ssl_match_hostname.py +159 -0
  80. build/lib/vendor/urllib3/util/ssltransport.py +276 -0
  81. build/lib/vendor/urllib3/util/timeout.py +275 -0
  82. build/lib/vendor/urllib3/util/url.py +471 -0
  83. build/lib/vendor/urllib3/util/util.py +42 -0
  84. build/lib/vendor/urllib3/util/wait.py +124 -0
  85. catocli/__init__.py +1 -1
  86. catocli/parsers/parserApiClient.py +7 -4
  87. {catocli-1.0.4.dist-info → catocli-1.0.5.dist-info}/METADATA +1 -1
  88. {catocli-1.0.4.dist-info → catocli-1.0.5.dist-info}/RECORD +92 -8
  89. {catocli-1.0.4.dist-info → catocli-1.0.5.dist-info}/top_level.txt +1 -0
  90. {catocli-1.0.4.dist-info → catocli-1.0.5.dist-info}/LICENSE +0 -0
  91. {catocli-1.0.4.dist-info → catocli-1.0.5.dist-info}/WHEEL +0 -0
  92. {catocli-1.0.4.dist-info → catocli-1.0.5.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1002 @@
1
+ #!/usr/bin/python
2
+ import datetime
3
+ import json
4
+ import ssl
5
+ import sys
6
+ import time
7
+ import urllib.parse
8
+ import urllib.request
9
+ import logging
10
+ from optparse import OptionParser
11
+ import os
12
+ import sys
13
+ import copy
14
+
15
+ api_call_count = 0
16
+ start = datetime.datetime.now()
17
+ catoApiIntrospection = {
18
+ "enums": {},
19
+ "scalars": {},
20
+ "objects": {},
21
+ "input_objects": {},
22
+ "unions": {},
23
+ "interfaces": {},
24
+ "unknowns": {}
25
+ }
26
+ catoApiSchema = {
27
+ "query": {},
28
+ "mutation": {}
29
+ }
30
+
31
+ def initParser():
32
+ if "CATO_TOKEN" not in os.environ:
33
+ print("Missing authentication, please set the CATO_TOKEN environment variable with your api key.")
34
+ exit()
35
+
36
+ # Process options
37
+ parser = OptionParser()
38
+ parser.add_option("-I", dest="ID", help="Account ID")
39
+ parser.add_option("-P", dest="prettify", action="store_true", help="Prettify output")
40
+ parser.add_option("-p", dest="print_entities", action="store_true", help="Print entity records")
41
+ parser.add_option("-v", dest="verbose", action="store_true", help="Print debug info")
42
+ (options, args) = parser.parse_args()
43
+ options.api_key = os.getenv("CATO_TOKEN")
44
+ if options.ID is None is None:
45
+ parser.print_help()
46
+ sys.exit()
47
+ if options.verbose:
48
+ logging.getLogger().setLevel(logging.DEBUG)
49
+ else:
50
+ logging.getLogger().setLevel(logging.INFO)
51
+ return options
52
+
53
+ def loadJSON(file):
54
+ CONFIG = {}
55
+ try:
56
+ with open(file, 'r') as data:
57
+ CONFIG = json.load(data)
58
+ logging.warning("Loaded "+file+" data")
59
+ return CONFIG
60
+ except:
61
+ logging.warning("File \""+file+"\" not found.")
62
+ exit()
63
+
64
+ def writeFile(fileName, data):
65
+ open(fileName, 'w+').close()
66
+ file=open(fileName,"w+")
67
+ file.write(data)
68
+
69
+ def openFile(fileName, readMode="rt"):
70
+ try:
71
+ with open(fileName, readMode) as f:
72
+ fileTxt = f.read()
73
+ f.closed
74
+ return fileTxt
75
+ except:
76
+ print('[ERROR] File path "'+fileName+'" in csv not found, or script unable to read.')
77
+ exit()
78
+
79
+ ############ parsing schema ############
80
+
81
+ def parseSchema(schema):
82
+ mutationOperationsTMP = {}
83
+ queryOperationsTMP = {}
84
+ for i, type in enumerate(schema["data"]["__schema"]["types"]):
85
+ if type["kind"] == "ENUM":
86
+ catoApiIntrospection["enums"][type["name"]] = copy.deepcopy(type)
87
+ elif type["kind"] == "SCALAR":
88
+ catoApiIntrospection["scalars"][type["name"]] = copy.deepcopy(type)
89
+ elif type["kind"] == "INPUT_OBJECT":
90
+ catoApiIntrospection["input_objects"][type["name"]] = copy.deepcopy(type)
91
+ elif type["kind"] == "INTERFACE":
92
+ catoApiIntrospection["interfaces"][type["name"]] = copy.deepcopy(type)
93
+ elif type["kind"] == "UNION":
94
+ catoApiIntrospection["unions"][type["name"]] = copy.deepcopy(type)
95
+ elif type["kind"] == "OBJECT":
96
+ if type["name"] == "Query":
97
+ for field in type["fields"]:
98
+ if field["name"] =="xdr":
99
+ queryOperationsTMP[field["name"]] = copy.deepcopy(field)
100
+ else:
101
+ catoApiSchema["query"]["query."+field["name"]] = copy.deepcopy(field)
102
+ # catoParserMapping["query"][field["name"]]
103
+ elif type["name"] == "Mutation":
104
+ for field in type["fields"]:
105
+ mutationOperationsTMP[field["name"]] = copy.deepcopy(field)
106
+ else:
107
+ catoApiIntrospection["objects"][type["name"]] = copy.deepcopy(type)
108
+
109
+ for queryType in queryOperationsTMP:
110
+ parentQueryOperationType = copy.deepcopy(queryOperationsTMP[queryType])
111
+ getChildOperations("query", parentQueryOperationType, parentQueryOperationType, "query." + queryType)
112
+
113
+ for mutationType in mutationOperationsTMP:
114
+ parentMutationOperationType = copy.deepcopy(mutationOperationsTMP[mutationType])
115
+ getChildOperations("mutation", parentMutationOperationType, parentMutationOperationType, "mutation." + mutationType)
116
+
117
+ for operationType in catoApiSchema:
118
+ for operationName in catoApiSchema[operationType]:
119
+ # if operationName=="query.xdr.stories":
120
+ childOperations = catoApiSchema[operationType][operationName]["childOperations"].keys() if "childOperations" in catoApiSchema[operationType][operationName] and catoApiSchema[operationType][operationName]!=None else []
121
+ parsedOperation = parseOperation(catoApiSchema[operationType][operationName],childOperations)
122
+ parsedOperation = getOperationArgs(parsedOperation["type"]["definition"],parsedOperation)
123
+ parsedOperation["path"] = operationName
124
+ for argName in parsedOperation["args"]:
125
+ arg = parsedOperation["args"][argName]
126
+ parsedOperation["operationArgs"][arg["varName"]] = arg
127
+ parsedOperation["variablesPayload"] = generateExampleVariables(parsedOperation)
128
+ writeFile("../models/"+operationName+".json",json.dumps(parsedOperation, indent=4, sort_keys=True))
129
+ writeFile("../queryPayloads/"+operationName+".json",json.dumps(generateGraphqlPayload(parsedOperation["variablesPayload"],parsedOperation,operationName),indent=4,sort_keys=True))
130
+ payload = generateGraphqlPayload(parsedOperation["variablesPayload"],parsedOperation,operationName)
131
+ writeFile("../queryPayloads/"+operationName+".txt",payload["query"])
132
+
133
+ def getChildOperations(operationType, curType, parentType, parentPath):
134
+ # Parse fields for nested args to map out all child operations
135
+ # This will separate fields like stories and story for query.xdr,
136
+ # and all fields which are actually sub operations from mutation.internetFirewall, etc
137
+ if "childOperations" not in parentType:
138
+ parentType["childOperations"] = {}
139
+ curOfType = None
140
+ if "kind" in curType:
141
+ curOfType = copy.deepcopy(catoApiIntrospection[curType["kind"].lower() + "s"][curType["name"]])
142
+ elif "type" in curType and curType["type"]["ofType"]==None:
143
+ curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["kind"].lower() + "s"][curType["type"]["name"]])
144
+ elif "type" in curType and curType["type"]["ofType"]["ofType"]==None:
145
+ curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["ofType"]["kind"].lower() + "s"][curType["type"]["ofType"]["name"]])
146
+ elif "type" in curType and curType["type"]["ofType"]["ofType"]["ofType"]==None:
147
+ curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["ofType"]["ofType"]["kind"].lower() + "s"][curType["type"]["ofType"]["ofType"]["name"]])
148
+ elif "type" in curType and curType["type"]["ofType"]["ofType"]["ofType"]["ofType"]==None:
149
+ curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["ofType"]["ofType"]["ofType"]["kind"].lower() + "s"][curType["type"]["ofType"]["ofType"]["ofType"]["name"]])
150
+ else:
151
+ curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["ofType"]["ofType"]["ofType"]["ofType"]["kind"].lower() + "s"][curType["type"]["ofType"]["ofType"]["ofType"]["ofType"]["name"]])
152
+ hasChildren = False
153
+
154
+ if "fields" in curOfType and curOfType["fields"] != None:
155
+ parentFields = []
156
+ for field in curOfType["fields"]:
157
+ curFieldObject = copy.deepcopy(field)
158
+ if "args" in curFieldObject and len(curFieldObject["args"])>0:
159
+ hasChildren = True
160
+ curParentType = copy.deepcopy(parentType)
161
+ curFieldObject["args"] = getNestedArgDefinitions(curFieldObject["args"], curFieldObject["name"],None,None)
162
+ curParentType["childOperations"][curFieldObject["name"]] = curFieldObject
163
+ getChildOperations(operationType, curFieldObject, curParentType, parentPath + "." + curFieldObject["name"])
164
+ if not hasChildren:
165
+ catoApiSchema[operationType][parentPath] = parentType
166
+
167
+ def getNestedArgDefinitions(argsAry, parentParamPath, childOperations, parentFields):
168
+ newArgsList = {}
169
+ for arg in argsAry:
170
+ curParamPath = arg["name"] if (parentParamPath == None or parentParamPath == "") else parentParamPath.replace("___",".") + "." + arg["name"]
171
+ if "path" in arg and '.' not in arg["path"]:
172
+ arg["child"] = True
173
+ arg["parent"] = arg["path"]
174
+ arg["type"] = getOfType(arg["type"], { "non_null": False, "kind": [], "name": None }, curParamPath, childOperations, parentFields)
175
+ arg["path"] = curParamPath
176
+ arg["id_str"] = curParamPath.replace(".","___")
177
+ if isinstance(arg["type"]["kind"], list):
178
+ arg["required"] = True if arg["type"]["kind"][0] == "NON_NULL" else False
179
+ else:
180
+ arg["required"] = True if arg["type"]["kind"] == "NON_NULL" else False
181
+ required1 = "!" if arg["required"] else ""
182
+ required2 = "!" if "NON_NULL" in arg["type"]["kind"][1:] else ""
183
+ if "SCALAR" in arg["type"]["kind"] or "ENUM" in arg["type"]["kind"]:
184
+ arg["varName"] = renderCamelCase(arg["name"])
185
+ # arg["id_str"] = arg["varName"]
186
+ else:
187
+ arg["varName"] = renderCamelCase(arg["type"]["name"])
188
+ arg["responseStr"] = arg["name"] + ":$" + arg["varName"] + " "
189
+ if "LIST" in arg["type"]["kind"]:
190
+ arg["requestStr"] = "$" + arg["varName"] + ":" + "[" + arg["type"]["name"] + required2 + "]" + required1 + " "
191
+ else:
192
+ arg["requestStr"] = "$" + arg["varName"] + ":" + arg["type"]["name"] + required1 + " "
193
+ newArgsList[arg["id_str"]] = arg
194
+ # print("getNestedArgDefinitions()",newArgsList.keys())
195
+ return newArgsList
196
+
197
+ def getOfType(curType, ofType, parentParamPath, childOperations, parentFields):
198
+ ofType["kind"].append(copy.deepcopy(curType["kind"]))
199
+ curParamPath = "" if (parentParamPath == None) else parentParamPath + "___"
200
+ if curType["ofType"] != None:
201
+ ofType = getOfType(copy.deepcopy(curType["ofType"]), ofType, parentParamPath,childOperations,parentFields)
202
+ else:
203
+ ofType["name"] = curType["name"]
204
+ parentFields = []
205
+ if "definition" in ofType and "fields" in ofType["definition"] and ofType["definition"]["fields"]!=None:
206
+ for fieldName in ofType["definition"]["fields"]:
207
+ field = ofType["definition"]["fields"][fieldName]
208
+ parentFields.append(field["name"])
209
+ if "INPUT_OBJECT" in ofType["kind"]:
210
+ ofType["indexType"] = "input_object"
211
+ ofType["definition"] = copy.deepcopy(catoApiIntrospection["input_objects"][ofType["name"]])
212
+ if ofType["definition"]["inputFields"] != None:
213
+ ofType["definition"]["inputFields"] = getNestedFieldDefinitions(copy.deepcopy(ofType["definition"]["inputFields"]), curParamPath, childOperations, parentFields)
214
+ elif "UNION" in ofType["kind"]:
215
+ ofType["indexType"] = "interface"
216
+ ofType["definition"] = copy.deepcopy(catoApiIntrospection["unions"][ofType["name"]])
217
+ if ofType["definition"]["possibleTypes"] != None:
218
+ ofType["definition"]["possibleTypes"] = getNestedInterfaceDefinitions(copy.deepcopy(ofType["definition"]["possibleTypes"]), curParamPath,childOperations, parentFields)
219
+ # strip out each nested interface attribute from parent oftype fields,
220
+ # this is to prevent duplicate fields causing the query to fail
221
+ for interfaceName in ofType["definition"]["possibleTypes"]:
222
+ possibleType = ofType["definition"]["possibleTypes"][interfaceName]
223
+ if ofType["definition"]["fields"]!=None:
224
+ for fieldName in ofType["definition"]["fields"]:
225
+ field = ofType["definition"]["fields"][fieldName]
226
+ nestedFieldPath = parentParamPath + interfaceName + "___" + field["name"]
227
+ # aliasLogic
228
+ # if field["name"] in parentFields:
229
+ # field["alias"] = renderCamelCase(interfaceName+"."+field["name"])+": "+field["name"]
230
+ # if "SCALAR" in field["type"]["kind"] or "ENUM" in field["type"]["kind"]:
231
+ # possibleType["fields"][nestedFieldPath]["alias"] = renderCamelCase(interfaceName+"."+field["name"])+": "+field["name"]
232
+ # else:
233
+ # possibleType["fields"][nestedFieldPath]["alias"] = renderCamelCase(field["type"]["name"])+": "+field["name"]
234
+ ofType["definition"]["possibleTypes"][interfaceName] = copy.deepcopy(possibleType)
235
+ elif "OBJECT" in ofType["kind"]:
236
+ ofType["indexType"] = "object"
237
+ ofType["definition"] = copy.deepcopy(catoApiIntrospection["objects"][ofType["name"]])
238
+ if ofType["definition"]["fields"] != None and childOperations!=None:
239
+ ofType["definition"]["fields"] = checkForChildOperation(copy.deepcopy(ofType["definition"]["fields"]),childOperations)
240
+ ofType["definition"]["fields"] = getNestedFieldDefinitions(copy.deepcopy(ofType["definition"]["fields"]), curParamPath,childOperations, parentFields)
241
+ if ofType["definition"]["interfaces"] != None:
242
+ ofType["definition"]["interfaces"] = getNestedInterfaceDefinitions(copy.deepcopy(ofType["definition"]["interfaces"]), curParamPath,childOperations, parentFields)
243
+ elif "INTERFACE" in ofType["kind"]:
244
+ ofType["indexType"] = "interface"
245
+ ofType["definition"] = copy.deepcopy(catoApiIntrospection["interfaces"][ofType["name"]])
246
+ if ofType["definition"]["fields"] != None:
247
+ ofType["definition"]["fields"] = getNestedFieldDefinitions(copy.deepcopy(ofType["definition"]["fields"]), curParamPath, childOperations, parentFields)
248
+ if ofType["definition"]["possibleTypes"] != None:
249
+ ofType["definition"]["possibleTypes"] = getNestedInterfaceDefinitions(copy.deepcopy(ofType["definition"]["possibleTypes"]), curParamPath, childOperations, parentFields)
250
+ for interfaceName in ofType["definition"]["possibleTypes"]:
251
+ possibleType = copy.deepcopy(ofType["definition"]["possibleTypes"][interfaceName])
252
+ for fieldName in ofType["definition"]["fields"]:
253
+ field = ofType["definition"]["fields"][fieldName]
254
+ nestedFieldPath = parentParamPath + interfaceName + "." + field["name"]
255
+ nestedFieldPath = nestedFieldPath.replace(".","___")
256
+ if "args" in field and len(field["args"])>0:
257
+ field["args"] = getNestedArgDefinitions(copy.deepcopy(field["args"]), nestedFieldPath, curParamPath, parentFields)
258
+ # aliasLogic MUST
259
+ # CatoEndpointUser
260
+ if field["name"] in possibleType["fields"] and possibleType["fields"][field["name"]] != None:
261
+ # del possibleType["fields"][field["name"]]
262
+ if "SCALAR" in field["type"]["kind"] or "ENUM" in field["type"]["kind"]:
263
+ possibleType["fields"][field["name"]]["alias"] = renderCamelCase(interfaceName+"."+field["name"])+": "+field["name"]
264
+ else:
265
+ possibleType["fields"][field["name"]]["alias"] = renderCamelCase(field["type"]["name"])+": "+field["name"]
266
+ ofType["definition"]["possibleTypes"][interfaceName] = copy.deepcopy(possibleType)
267
+ if ofType["definition"]["interfaces"] != None:
268
+ ofType["definition"]["interfaces"] = getNestedInterfaceDefinitions(copy.deepcopy(ofType["definition"]["interfaces"]), curParamPath,childOperations, parentFields)
269
+ elif "ENUM" in ofType["kind"]:
270
+ ofType["indexType"] = "enum"
271
+ ofType["definition"] = copy.deepcopy(catoApiIntrospection["enums"][ofType["name"]])
272
+ return ofType
273
+
274
+ def getNestedFieldDefinitions(fieldsAry, parentParamPath,childOperations, parentFields):
275
+ newFieldsList = {}
276
+ for field in fieldsAry:
277
+ if isinstance(field,str):
278
+ field = fieldsAry[field]
279
+ curParamPath = field["name"] if (parentParamPath == None) else (parentParamPath.replace("___",".") + field["name"])
280
+ # curParamPath = field["name"] if (parentParamPath == None) else (parentParamPath + "." + field["name"])
281
+ field["type"] = getOfType(field["type"], { "non_null": False, "kind": [], "name": None }, curParamPath,childOperations, parentFields)
282
+ field["path"] = curParamPath
283
+ field["id_str"] = curParamPath.replace(".","___")
284
+ if isinstance(field["type"]["kind"], list):
285
+ field["required"] = True if field["type"]["kind"][0] == "NON_NULL" else False
286
+ else:
287
+ field["required"] = True if field["type"]["kind"] == "NON_NULL" else False
288
+ required1 = "!" if field["required"] else ""
289
+ required2 = "!" if field["type"]["kind"][1:] == "NON_NULL" else ""
290
+ if "SCALAR" in field["type"]["kind"] or "ENUM" in field["type"]["kind"]:
291
+ field["varName"] = renderCamelCase(field["name"])
292
+ # field["id_str"] = field["varName"]
293
+ else:
294
+ field["varName"] = renderCamelCase(field["type"]["name"])
295
+ field["responseStr"] = field["name"] + ":$" + field["varName"] + " "
296
+ if "LIST" in field["type"]["kind"]:
297
+ field["requestStr"] = "$" + field["varName"] + ":" + "[" + field["type"]["name"] + required2 + "]" + required1 + " "
298
+ else:
299
+ field["requestStr"] = "$" + field["varName"] + ":" + field["type"]["name"] + required1 + " "
300
+ if "args" in field:
301
+ field["args"] = getNestedArgDefinitions(field["args"], field["name"],childOperations, parentFields)
302
+ ## aliasLogic must
303
+ if parentFields!=None and field["name"] in parentFields and "SCALAR" not in field["type"]["kind"]:
304
+ # if field["name"]=="records":
305
+ # raise ValueError('A very specific bad thing happened.')
306
+ # print(json.dumps(field,indent=2,sort_keys=True))
307
+ # print(field["path"],parentFields)
308
+ # field["alias"] = renderCamelCase(field["type"]["name"]+"."+field["name"])+": "+field["name"]
309
+ field["alias"] = renderCamelCase(field["type"]["name"]+"."+field["name"])+": "+field["name"]
310
+ if "records.fields" not in field["path"]:
311
+ newFieldsList[field["name"]] = field
312
+ # for (fieldPath in newFieldList) {
313
+ # var field = newFieldList[fieldPath];
314
+ # if (curOperationObj.fieldTypes && field.type.name != null) curOperationObj.fieldTypes[field.type.name] = true;
315
+ # field.type = getOfType(field.type, { non_null: false, kind: [], name: null }, field.path);
316
+ # }
317
+ return newFieldsList
318
+
319
+ def getNestedInterfaceDefinitions(possibleTypesAry, parentParamPath,childOperations, parentFields):
320
+ curInterfaces = {}
321
+ for possibleType in possibleTypesAry:
322
+ if "OBJECT" in possibleType["kind"]:
323
+ curInterfaces[possibleType["name"]] = copy.deepcopy(catoApiIntrospection["objects"][possibleType["name"]])
324
+ # for curInterface in curInterfaces:
325
+ # curParamPath = "" if parentParamPath == None else parentParamPath + curInterface["name"] + "___"
326
+ for curInterfaceName in curInterfaces:
327
+ curInterface = curInterfaces[curInterfaceName]
328
+ curParamPath = "" if parentParamPath == None else parentParamPath + curInterface["name"] + "___"
329
+ if "fields" in curInterface and curInterface["fields"] != None:
330
+ curInterface["fields"] = getNestedFieldDefinitions(copy.deepcopy(curInterface["fields"]), curParamPath,childOperations, parentFields)
331
+ if "inputFields" in curInterface and curInterface["inputFields"] != None:
332
+ curInterface["inputFields"] = getNestedFieldDefinitions(copy.deepcopy(curInterface["inputFields"]), curParamPath,childOperations, parentFields)
333
+ if "interfaces" in curInterface and curInterface["interfaces"] != None:
334
+ curInterface["interfaces"] = getNestedInterfaceDefinitions(copy.deepcopy(curInterface["interfaces"]), curParamPath,childOperations, parentFields)
335
+ if "possibleTypes" in curInterface and curInterface["possibleTypes"] != None:
336
+ curInterface["possibleTypes"] = getNestedInterfaceDefinitions(copy.deepcopy(curInterface["possibleTypes"]), curParamPath,childOperations, parentFields)
337
+ return curInterfaces
338
+
339
+ def parseOperation(curOperation,childOperations):
340
+ if "operationArgs" not in curOperation:
341
+ curOperation["operationArgs"] = {}
342
+ curOperation["fieldTypes"] = {}
343
+ curOfType = getOfType(curOperation["type"], { "non_null": False, "kind": [], "name": None }, None,childOperations,None)
344
+ curOperation["type"] = copy.deepcopy(curOfType)
345
+ if curOfType["name"] in catoApiIntrospection["objects"]:
346
+ curOperation["args"] = getNestedArgDefinitions(curOperation["args"], None,childOperations,None)
347
+ curOperation["type"]["definition"] = copy.deepcopy(catoApiIntrospection["objects"][curOperation["type"]["name"]])
348
+ if "fields" in curOperation["type"]["definition"] and curOperation["type"]["definition"]["fields"] != None:
349
+ # aliasLogic
350
+ # parentFields = []
351
+ # for field in curOperation["type"]["definition"]["fields"]:
352
+ # parentFields.append(field["name"])
353
+ curOperation["type"]["definition"]["fields"] = checkForChildOperation(copy.deepcopy(curOperation["type"]["definition"]["fields"]),childOperations)
354
+ curOperation["type"]["definition"]["fields"] = copy.deepcopy(getNestedFieldDefinitions(curOperation["type"]["definition"]["fields"], None,childOperations,[]))
355
+ if "inputFields" in curOperation["type"]["definition"] and curOperation["type"]["definition"]["inputFields"] != None:
356
+ parentFields = curOperation["type"]["definition"]["inputFields"].keys()
357
+ curOperation["type"]["definition"]["inputFields"] = copy.deepcopy(getNestedFieldDefinitions(curOperation["type"]["definition"]["inputFields"], None,childOperations,parentFields))
358
+ return curOperation
359
+
360
+ def checkForChildOperation(fieldsAry,childOperations):
361
+ newFieldList = {}
362
+ subOperation = False
363
+ for i, field in enumerate(fieldsAry):
364
+ if field["name"] in childOperations:
365
+ subOperation = field
366
+ newFieldList[field["name"]] = copy.deepcopy(field)
367
+ if subOperation != False:
368
+ newFieldList = {}
369
+ newFieldList[subOperation["name"]] = subOperation
370
+ return newFieldList
371
+
372
+ def getOperationArgs(curType,curOperation):
373
+ if curType.get('fields'):
374
+ for fieldName in curType["fields"]:
375
+ field = curType["fields"][fieldName]
376
+ ## aliasLogic
377
+ if "type" in field and "definition" in field["type"]:
378
+ curOperation["fieldTypes"][field["type"]["definition"]["name"]] = True
379
+ curOperation = getOperationArgs(field["type"]["definition"],curOperation)
380
+ if "args" in field:
381
+ for argName in field["args"]:
382
+ arg = field["args"][argName]
383
+ curOperation["operationArgs"][arg["varName"]] = arg
384
+ if "type" in arg and "definition" in arg["type"]:
385
+ curOperation = getOperationArgs(arg["type"]["definition"],curOperation)
386
+ if curType.get('inputFields'):
387
+ for inputFieldName in curType["inputFields"]:
388
+ inputField = curType["inputFields"][inputFieldName]
389
+ if "type" in inputField and "definition" in inputField["type"]:
390
+ curOperation["fieldTypes"][inputField["type"]["definition"]["name"]] = True
391
+ curOperation = getOperationArgs(inputField["type"]["definition"],curOperation)
392
+ if "args" in inputField:
393
+ for argName in inputField["args"]:
394
+ arg = inputField["args"][argName]
395
+ curOperation["operationArgs"][arg["varName"]] = arg
396
+ if "type" in arg and "definition" in arg["type"]:
397
+ curOperation = getOperationArgs(arg["type"]["definition"],curOperation)
398
+ if curType.get('interfaces'):
399
+ for interface in curType["interfaces"]:
400
+ if "type" in interface and "definition" in interface["type"]:
401
+ curOperation["fieldTypes"][interface["type"]["definition"]["name"]] = True
402
+ curOperation = getOperationArgs(interface["type"]["definition"],curOperation)
403
+ if "args" in interface:
404
+ for argName in interface["args"]:
405
+ arg = interface["args"][argName]
406
+ curOperation["operationArgs"][arg["varName"]] = arg
407
+ if "type" in arg and "definition" in arg["type"]:
408
+ curOperation = getOperationArgs(arg["type"]["definition"],curOperation)
409
+ if curType.get('possibleTypes'):
410
+ for possibleTypeName in curType["possibleTypes"]:
411
+ possibleType = curType["possibleTypes"][possibleTypeName]
412
+ curOperation = getOperationArgs(possibleType,curOperation)
413
+ if possibleType.get('fields'):
414
+ for fieldName in possibleType["fields"]:
415
+ field = possibleType["fields"][fieldName]
416
+ ## aliasLogic
417
+ # if "type" in curOperation and "definition" in curOperation["type"] and "fields" in curOperation["type"]["definition"] and field["name"] in curOperation["type"]["definition"]["fields"]:
418
+ # # if field["type"]["definition"]["fields"]==None or field["type"]["definition"]["inputFields"]:
419
+ # # if "SCALAR" not in field["type"]["kind"] or "ENUM" not in field["type"]["kind"]:
420
+ # field["alias"] = renderCamelCase(possibleTypeName+"."+field["name"])+": "+field["name"]
421
+ if "args" in possibleType:
422
+ for argName in possibleType["args"]:
423
+ arg = possibleType["args"][argName]
424
+ curOperation["operationArgs"][arg["varName"]] = arg
425
+ if "type" in arg and "definition" in arg["type"]:
426
+ curOperation = getOperationArgs(arg["type"]["definition"],curOperation)
427
+ return curOperation
428
+
429
+ def renderCamelCase(pathStr):
430
+ str = ""
431
+ pathAry = pathStr.split(".")
432
+ for i, path in enumerate(pathAry):
433
+ if i == 0:
434
+ str += path[0].lower() + path[1:]
435
+ else:
436
+ str += path[0].upper() + path[1:]
437
+ return str
438
+
439
+ ## Functions to create sample nested variable objects for cli arguments ##
440
+ def generateExampleVariables(operation):
441
+ variablesObj = {}
442
+ for argName in operation["operationArgs"]:
443
+ arg = operation["operationArgs"][argName]
444
+ if "SCALAR" in arg["type"]["kind"] or "ENUM" in arg["type"]["kind"]:
445
+ variablesObj[arg["name"]] = renderInputFieldVal(arg)
446
+ else:
447
+ argTD = arg["type"]["definition"]
448
+ variablesObj[arg["varName"]] = {}
449
+ if "inputFields" in argTD and argTD["inputFields"] != None:
450
+ for inputFieldName in argTD["inputFields"]:
451
+ inputField = argTD["inputFields"][inputFieldName]
452
+ variablesObj[arg["varName"]][inputField["varName"]] = parseNestedArgFields(inputField)
453
+ if "fields" in argTD and argTD["fields"] != None:
454
+ for fieldName in argTD["fields"]:
455
+ field = argTD["fields"][fieldName]
456
+ variablesObj[arg["varName"]][field["varName"]] = parseNestedArgFields(field)
457
+ if "possibleTypes" in argTD and argTD["possibleTypes"] != None:
458
+ for possibleTypeName in argTD["possibleTypes"]:
459
+ possibleType = argTD["possibleTypes"][possibleTypeName]
460
+ variablesObj[arg["varName"]][possibleType["varName"]] = parseNestedArgFields(possibleTypeName)
461
+ if "accountID" in variablesObj:
462
+ del variablesObj["accountID"]
463
+ if "accountId" in variablesObj:
464
+ del variablesObj["accountId"]
465
+ return variablesObj
466
+
467
+ def parseNestedArgFields(fieldObj):
468
+ subVariableObj = {}
469
+ if "SCALAR" in fieldObj["type"]["kind"] or "ENUM" in fieldObj["type"]["kind"]:
470
+ subVariableObj[fieldObj["name"]] = renderInputFieldVal(fieldObj)
471
+ else:
472
+ fieldTD = fieldObj["type"]["definition"]
473
+ if "inputFields" in fieldTD and fieldTD["inputFields"] != None:
474
+ for inputFieldName in fieldTD["inputFields"]:
475
+ inputField = fieldTD["inputFields"][inputFieldName]
476
+ subVariableObj[inputField["name"]] = parseNestedArgFields(inputField)
477
+ if "fields" in fieldTD and fieldTD["fields"] != None:
478
+ for fieldName in fieldTD["fields"]:
479
+ field = fieldTD["fields"][fieldName]
480
+ subVariableObj[field["name"]] = parseNestedArgFields(field)
481
+ if "possibleTypes" in fieldTD and fieldTD["possibleTypes"] != None:
482
+ for possibleTypeName in fieldTD["possibleTypes"]:
483
+ possibleType = fieldTD["possibleTypes"][possibleTypeName]
484
+ subVariableObj[possibleType["name"]] = parseNestedArgFields(possibleTypeName)
485
+ return subVariableObj
486
+
487
+ def renderInputFieldVal(arg):
488
+ value = "string"
489
+ if "SCALAR" in arg["type"]["kind"]:
490
+ if "LIST" in arg["type"]["kind"]:
491
+ value = [arg["type"]["name"]]
492
+ else:
493
+ value = arg["type"]["name"]
494
+ elif "ENUM" in arg["type"]["kind"]:
495
+ value = "enum("+arg["type"]["name"]+")"
496
+ # arg["type"]["definition"]["enumValues"][0]["name"]
497
+ return value
498
+
499
+ def writeCliDriver(catoApiSchema):
500
+ parsersIndex = {}
501
+ for operationType in catoApiSchema:
502
+ for operation in catoApiSchema[operationType]:
503
+ operationNameAry = operation.split(".")
504
+ parsersIndex[operationNameAry[0]+"_"+operationNameAry[1]] = True
505
+ parsers = list(parsersIndex.keys())
506
+ cliDriverStr = """
507
+ import os
508
+ import argparse
509
+ import json
510
+ import catocli
511
+ from graphql_client import Configuration
512
+ from graphql_client.api_client import ApiException
513
+ from ..parsers.parserApiClient import get_help
514
+ import sys
515
+ sys.path.insert(0, 'vendor')
516
+ import urllib3
517
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
518
+ if "CATO_TOKEN" not in os.environ:
519
+ print("Missing authentication, please set the CATO_TOKEN environment variable with your api key.")
520
+ exit()
521
+ CATO_TOKEN = os.getenv("CATO_TOKEN")
522
+ CATO_DEBUG = bool(os.getenv("CATO_DEBUG", False))
523
+ from ..parsers.raw import raw_parse
524
+ #from ..parsers.custom import custom_parse
525
+ from ..parsers.query_siteLocation import query_siteLocation_parse
526
+ """
527
+ for parserName in parsers:
528
+ cliDriverStr += "from ..parsers."+parserName+" import "+parserName+"_parse\n"
529
+
530
+ cliDriverStr += """
531
+ configuration = Configuration()
532
+ configuration.verify_ssl = False
533
+ configuration.api_key["x-api-key"] = CATO_TOKEN
534
+ configuration.host = "{}".format(catocli.__cato_host__)
535
+ configuration.debug = CATO_DEBUG
536
+ configuration.version = "{}".format(catocli.__version__)
537
+
538
+ parser = argparse.ArgumentParser(prog='catocli', usage='%(prog)s <operationType> <operationName> [options]', description="CLI for query on CATO via API.")
539
+ parser.add_argument('--version', action='version', version=catocli.__version__)
540
+ subparsers = parser.add_subparsers()
541
+ #custom_parsers = custom_parse(subparsers)
542
+ raw_parsers = subparsers.add_parser('raw', help='Raw GraphQL', usage=get_help("raw"))
543
+ raw_parser = raw_parse(raw_parsers)
544
+ query_parser = subparsers.add_parser('query', help='Query', usage='catocli query <operationName> [options]')
545
+ query_subparsers = query_parser.add_subparsers(description='valid subcommands', help='additional help')
546
+ query_siteLocation_parser = query_siteLocation_parse(query_subparsers)
547
+ mutation_parser = subparsers.add_parser('mutation', help='Mutation', usage='catocli mutation <operationName> [options]')
548
+ mutation_subparsers = mutation_parser.add_subparsers(description='valid subcommands', help='additional help')
549
+
550
+ """
551
+ for parserName in parsers:
552
+ cliDriverStr += parserName+"_parser = "+parserName+"_parse("+parserName.split("_").pop(0)+"_subparsers)\n"
553
+
554
+ cliDriverStr += """
555
+
556
+ def main(args=None):
557
+ args = parser.parse_args(args=args)
558
+ try:
559
+ response = args.func(args, configuration)
560
+
561
+ if type(response) == ApiException:
562
+ print("ERROR! Status code: {}".format(response.status))
563
+ print(response)
564
+ else:
565
+ if response!=None:
566
+ print(json.dumps(response[0], sort_keys=True, indent=4))
567
+ except AttributeError:
568
+ print('Missing arguments. Usage: catocli <operation> -h')
569
+ """
570
+ writeFile("../catocli/Utils/clidriver.py",cliDriverStr)
571
+
572
+ def writeOperationParsers(catoApiSchema):
573
+ parserMapping = {"query":{},"mutation":{}}
574
+ ## Write the raw query parser ##
575
+ cliDriverStr =f"""
576
+ from ..parserApiClient import createRawRequest, get_help
577
+
578
+ def raw_parse(raw_parser):
579
+ raw_parser.add_argument('json', help='Query, Variables and opertaionName in JSON format.')
580
+ raw_parser.add_argument('-t', const=True, default=False, nargs='?', help='Print test request preview without sending api call')
581
+ raw_parser.add_argument('-v', const=True, default=False, nargs='?', help='Verbose output')
582
+ raw_parser.add_argument('-p', const=True, default=False, nargs='?', help='Pretty print')
583
+ raw_parser.set_defaults(func=createRawRequest,operation_name='raw')
584
+ """
585
+ parserPath = "../catocli/parsers/raw"
586
+ if not os.path.exists(parserPath):
587
+ os.makedirs(parserPath)
588
+ writeFile(parserPath+"/__init__.py",cliDriverStr)
589
+
590
+ ## Write the siteLocation query parser ##
591
+ cliDriverStr =f"""
592
+ from ..parserApiClient import querySiteLocation, get_help
593
+
594
+ def query_siteLocation_parse(query_subparsers):
595
+ query_siteLocation_parser = query_subparsers.add_parser('siteLocation',
596
+ help='siteLocation local cli query',
597
+ usage=get_help("query_siteLocation"))
598
+
599
+ query_siteLocation_parser.add_argument('accountID', help='The Account ID.')
600
+ query_siteLocation_parser.add_argument('json', help='Variables in JSON format.')
601
+ query_siteLocation_parser.add_argument('-t', const=True, default=False, nargs='?',
602
+ help='Print test request preview without sending api call')
603
+ query_siteLocation_parser.add_argument('-v', const=True, default=False, nargs='?',
604
+ help='Verbose output')
605
+ query_siteLocation_parser.add_argument('-p', const=True, default=False, nargs='?',
606
+ help='Pretty print')
607
+ query_siteLocation_parser.set_defaults(func=querySiteLocation,operation_name='query.siteLocation')
608
+ """
609
+ parserPath = "../catocli/parsers/query_siteLocation"
610
+ if not os.path.exists(parserPath):
611
+ os.makedirs(parserPath)
612
+ writeFile(parserPath+"/__init__.py",cliDriverStr)
613
+
614
+ for operationType in parserMapping:
615
+ operationAry = catoApiSchema[operationType]
616
+ for operationName in operationAry:
617
+ parserMapping = getParserMapping(parserMapping,operationName,operationName,operationAry[operationName])
618
+ for operationType in parserMapping:
619
+ for operationName in parserMapping[operationType]:
620
+ parserName = operationType+"_"+operationName
621
+ parser = parserMapping[operationType][operationName]
622
+ cliDriverStr = f"""
623
+ from ..parserApiClient import createRequest, get_help
624
+
625
+ def {parserName}_parse({operationType}_subparsers):
626
+ {parserName}_parser = {operationType}_subparsers.add_parser('{operationName}',
627
+ help='{operationName}() {operationType} operation',
628
+ usage=get_help("{operationType}_{operationName}"))
629
+ """
630
+ if "path" in parser:
631
+ cliDriverStr += f"""
632
+ {parserName}_parser.add_argument('accountID', help='The Account ID.')
633
+ {parserName}_parser.add_argument('json', help='Variables in JSON format.')
634
+ {parserName}_parser.add_argument('-t', const=True, default=False, nargs='?',
635
+ help='Print test request preview without sending api call')
636
+ {parserName}_parser.add_argument('-v', const=True, default=False, nargs='?',
637
+ help='Verbose output')
638
+ {parserName}_parser.add_argument('-p', const=True, default=False, nargs='?',
639
+ help='Pretty print')
640
+ {parserName}_parser.set_defaults(func=createRequest,operation_name='{parserName.replace("_",".")}')
641
+ """
642
+ else:
643
+ cliDriverStr += renderSubParser(parser,operationType+"_"+operationName)
644
+ parserPath = "../catocli/parsers/"+parserName
645
+ if not os.path.exists(parserPath):
646
+ os.makedirs(parserPath)
647
+ writeFile(parserPath+"/__init__.py",cliDriverStr)
648
+
649
+ def renderSubParser(subParser,parentParserPath):
650
+ cliDriverStr = f"""
651
+ {parentParserPath}_subparsers = {parentParserPath}_parser.add_subparsers()
652
+ """
653
+ for subOperationName in subParser:
654
+ subOperation = subParser[subOperationName]
655
+ subParserPath = parentParserPath.replace(".","_")+"_"+subOperationName
656
+ cliDriverStr += f"""
657
+ {subParserPath}_parser = {parentParserPath}_subparsers.add_parser('{subOperationName}',
658
+ help='{subOperationName}() {parentParserPath.split('_').pop()} operation',
659
+ usage=get_help("{subParserPath}"))
660
+ """
661
+ if "path" in subOperation:
662
+ command = parentParserPath.replace("_"," ")+" "+subOperationName
663
+ cliDriverStr += f"""
664
+ {subParserPath}_parser.add_argument('accountID', help='The Account ID.')
665
+ {subParserPath}_parser.add_argument('json', help='Variables in JSON format.')
666
+ {subParserPath}_parser.add_argument('-t', const=True, default=False, nargs='?',
667
+ help='Print test request preview without sending api call')
668
+ {subParserPath}_parser.add_argument('-v', const=True, default=False, nargs='?',
669
+ help='Verbose output')
670
+ {subParserPath}_parser.add_argument('-p', const=True, default=False, nargs='?',
671
+ help='Pretty print')
672
+ {subParserPath}_parser.set_defaults(func=createRequest,operation_name='{subOperation["path"]}')
673
+ """
674
+ else:
675
+ cliDriverStr += renderSubParser(subOperation,subParserPath)
676
+ return cliDriverStr
677
+
678
+ def writeReadmes(catoApiSchema):
679
+ parserMapping = {"query":{},"mutation":{}}
680
+
681
+ ## Write the raw query readme ##
682
+ readmeStr = """
683
+ ## CATO-CLI - raw.graphql
684
+ [Click here](https://api.catonetworks.com/documentation/) for documentation on this operation.
685
+
686
+ ### Usage for raw.graphql
687
+
688
+ `catocli raw -h`
689
+
690
+ `catocli raw <json>`
691
+
692
+ `catocli raw "$(cat < rawGraphqQL.json)"`
693
+
694
+ `catocli raw '{ "query": "query operationNameHere($yourArgument:String!) { field1 field2 }", "variables": { "yourArgument": "string", "accountID": "10949" }, "operationName": "operationNameHere" } '`
695
+
696
+ `catocli raw '{ "query": "mutation operationNameHere($yourArgument:String!) { field1 field2 }", "variables": { "yourArgument": "string", "accountID": "10949" }, "operationName": "operationNameHere" } '`
697
+ """
698
+ parserPath = "../catocli/parsers/raw"
699
+ if not os.path.exists(parserPath):
700
+ os.makedirs(parserPath)
701
+ writeFile(parserPath+"/README.md",readmeStr)
702
+
703
+ ## Write the query.siteLocation readme ##
704
+ readmeStr = """
705
+
706
+ ## CATO-CLI - query.siteLocation:
707
+
708
+ ### Usage for query.siteLocation:
709
+
710
+ `catocli query siteLocation -h`
711
+
712
+ `catocli query siteLocation <accountID> <json>`
713
+
714
+ `catocli query siteLocation 12345 "$(cat < siteLocation.json)"`
715
+
716
+ `catocli query siteLocation 12345 '{"filters":[{"search": "Your city here","field":"city","operation":"exact"}]}'`
717
+
718
+ `catocli query siteLocation 12345 '{"filters":[{"search": "Your Country here","field":"countryName","operation":"startsWith"}]}'`
719
+
720
+ `catocli query siteLocation 12345 '{"filters":[{"search": "Your stateName here","field":"stateName","operation":"endsWith"}]}'`
721
+
722
+ `catocli query siteLocation 12345 '{filters:[{"search": "Your City here","field":"city","operation":"startsWith"},{"search": "Your StateName here","field":"stateName","operation":"endsWith"},{"search": "Your Country here","field":"countryName","operation":"contains"}}'`
723
+
724
+ #### Operation Arguments for query.siteLocation ####
725
+ `accountID` [ID] - (optional) Unique Identifier of Account.
726
+ `filters[]` [Array] - (optional) Array of objects consisting of `search`, `field` and `operation` attributes.
727
+ `filters[].search` [String] - (required) String to match countryName, stateName, or city specificed in `filters[].field`.
728
+ `filters[].field` [String] - (required) Specify field to match query against, defaults to look for any. Possible values: `countryName`, `stateName`, or `city`.
729
+ `filters[].operation` [string] - (required) If a field is specified, operation to match the field value. Possible values: `startsWith`,`endsWith`,`exact`, `contains`.
730
+ """
731
+ parserPath = "../catocli/parsers/query_siteLocation"
732
+ if not os.path.exists(parserPath):
733
+ os.makedirs(parserPath)
734
+ writeFile(parserPath+"/README.md",readmeStr)
735
+
736
+ for operationType in parserMapping:
737
+ operationAry = catoApiSchema[operationType]
738
+ for operationName in operationAry:
739
+ parserMapping = getParserMapping(parserMapping,operationName,operationName,operationAry[operationName])
740
+ for operationType in parserMapping:
741
+ for operationName in parserMapping[operationType]:
742
+ parserName = operationType+"_"+operationName
743
+ parser = parserMapping[operationType][operationName]
744
+ operationPath = operationType+"."+operationName
745
+ operationCmd = operationType+" "+operationName
746
+ readmeStr = f"""
747
+ ## CATO-CLI - {operationPath}:
748
+ [Click here](https://api.catonetworks.com/documentation/#{operationType}-{operationName}) for documentation on this operation.
749
+
750
+ ### Usage for {operationPath}:
751
+
752
+ `catocli {operationCmd} -h`
753
+ """
754
+ if "path" in parser:
755
+ readmeStr += f"""
756
+ `catocli {operationCmd} <accountID> <json>`
757
+
758
+ `catocli {operationCmd} 12345 "$(cat < {operationName}.json)"`
759
+
760
+ `catocli {operationCmd} 12345 '{json.dumps(parser["example"])}'`
761
+
762
+ #### Operation Arguments for {operationPath} ####
763
+ """
764
+ for argName in parser["args"]:
765
+ arg = parser["args"][argName]
766
+ readmeStr += '`'+argName+'` ['+arg["type"]+'] - '
767
+ readmeStr += '('+arg["required"]+') '+arg["description"]+' '
768
+ readmeStr += 'Default Value: '+str(arg["values"]) if len(arg["values"])>0 else ""
769
+ readmeStr += "\n"
770
+ parserPath = "../catocli/parsers/"+parserName
771
+ if not os.path.exists(parserPath):
772
+ os.makedirs(parserPath)
773
+ writeFile(parserPath+"/README.md",readmeStr)
774
+ else:
775
+ parserPath = "../catocli/parsers/"+parserName
776
+ if not os.path.exists(parserPath):
777
+ os.makedirs(parserPath)
778
+ writeFile(parserPath+"/README.md",readmeStr)
779
+ renderSubReadme(parser,operationType,operationType+"."+operationName)
780
+
781
+ def renderSubReadme(subParser,operationType,parentOperationPath):
782
+ for subOperationName in subParser:
783
+ subOperation = subParser[subOperationName]
784
+ subOperationPath = parentOperationPath+"."+subOperationName
785
+ subOperationCmd = parentOperationPath.replace("."," ")+" "+subOperationName
786
+ parserPath = "../catocli/parsers/"+subOperationPath.replace(".","_")
787
+ readmeStr = f"""
788
+ ## CATO-CLI - {parentOperationPath}.{subOperationName}:
789
+ [Click here](https://api.catonetworks.com/documentation/#{operationType}-{subOperationName}) for documentation on this operation.
790
+
791
+ ### Usage for {subOperationPath}:
792
+
793
+ `catocli {subOperationCmd} -h`
794
+ """
795
+ if "path" in subOperation:
796
+ readmeStr += f"""
797
+ `catocli {subOperationCmd} <accountID> <json>`
798
+
799
+ `catocli {subOperationCmd} 12345 "$(cat < {subOperationName}.json)"`
800
+
801
+ `catocli {subOperationCmd} 12345 '{json.dumps(subOperation["example"])}'`
802
+
803
+ #### Operation Arguments for {subOperationPath} ####
804
+ """
805
+ for argName in subOperation["args"]:
806
+ arg = subOperation["args"][argName]
807
+ readmeStr += '`'+argName+'` ['+arg["type"]+'] - '
808
+ readmeStr += '('+arg["required"]+') '+arg["description"]+' '
809
+ readmeStr += 'Default Value: '+str(arg["values"]) if len(arg["values"])>0 else ""
810
+ readmeStr += "\n"
811
+ if not os.path.exists(parserPath):
812
+ os.makedirs(parserPath)
813
+ writeFile(parserPath+"/README.md",readmeStr)
814
+ else:
815
+ if not os.path.exists(parserPath):
816
+ os.makedirs(parserPath)
817
+ writeFile(parserPath+"/README.md",readmeStr)
818
+ renderSubReadme(subOperation,operationType,subOperationPath)
819
+
820
+ def getParserMapping(curParser,curPath,operationFullPath,operation):
821
+ parserObj = {
822
+ "path":operationFullPath,
823
+ "args":{},
824
+ # "example":"N/A",
825
+ "example":operation["variablesPayload"]
826
+ }
827
+ for argName in operation["operationArgs"]:
828
+ arg = operation["operationArgs"][argName]
829
+ values = []
830
+ if "definition" in arg["type"] and "enumValues" in arg["type"]["definition"] and arg["type"]["definition"]["enumValues"]!=None:
831
+ for enumValue in arg["type"]["definition"]["enumValues"]:
832
+ values.append(enumValue["name"])
833
+ parserObj["args"][arg["varName"]] = {
834
+ "name":arg["name"],
835
+ "description":"N/A" if arg["description"]==None else arg["description"],
836
+ "type":arg["type"]["name"]+("[]" if "LIST" in arg["type"]["kind"] else ""),
837
+ "required": "required" if arg["required"]==True else "optional",
838
+ "values":values
839
+ }
840
+ pAry = curPath.split(".")
841
+ pathCount = len(curPath.split("."))
842
+ if pAry[0] not in curParser:
843
+ curParser[pAry[0]] = {}
844
+ if pathCount == 2:
845
+ curParser[pAry[0]][pAry[1]] = parserObj
846
+ else:
847
+ if pAry[1] not in curParser[pAry[0]]:
848
+ curParser[pAry[0]][pAry[1]] = {}
849
+ if pathCount == 3:
850
+ curParser[pAry[0]][pAry[1]][pAry[2]] = parserObj
851
+ else:
852
+ if pAry[2] not in curParser[pAry[0]][pAry[1]]:
853
+ curParser[pAry[0]][pAry[1]][pAry[2]] = {}
854
+ if pathCount == 4:
855
+ curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]] = parserObj
856
+ else:
857
+ if pAry[3] not in curParser[pAry[0]][pAry[1]][pAry[2]]:
858
+ curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]] = {}
859
+ if pathCount == 5:
860
+ curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]][pAry[4]] = parserObj
861
+ else:
862
+ if pAry[4] not in curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]]:
863
+ curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]][pAry[4]] = {}
864
+ if pathCount == 6:
865
+ curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]][pAry[4]][pAry[5]] = parserObj
866
+ return curParser
867
+
868
+ def send(api_key,query,variables={},operationName=None):
869
+ headers = { 'x-api-key': api_key,'Content-Type':'application/json'}
870
+ no_verify = ssl._create_unverified_context()
871
+ request = urllib.request.Request(url='https://api.catonetworks.com/api/v1/graphql2',
872
+ data=json.dumps(query).encode("ascii"),headers=headers)
873
+ response = urllib.request.urlopen(request, context=no_verify, timeout=60)
874
+ result_data = response.read()
875
+ result = json.loads(result_data)
876
+ if "errors" in result:
877
+ logging.warning(f"API error: {result_data}")
878
+ return False,result
879
+ return True,result
880
+
881
+
882
+ ################### adding functions local to generate dynamic payloads ####################
883
+ def generateGraphqlPayload(variablesObj,operation,operationName):
884
+ indent = " "
885
+ queryStr = ""
886
+ variableStr = ""
887
+ for varName in variablesObj:
888
+ if (varName in operation["operationArgs"]):
889
+ variableStr += operation["operationArgs"][varName]["requestStr"]
890
+ operationAry = operationName.split(".")
891
+ operationType = operationAry.pop(0)
892
+ queryStr = operationType + " "
893
+ queryStr += renderCamelCase(".".join(operationAry))
894
+ queryStr += " ( " + variableStr + ") {\n"
895
+ queryStr += indent + operation["name"] + " ( "
896
+ for argName in operation["args"]:
897
+ arg = operation["args"][argName]
898
+ if arg["varName"] in variablesObj:
899
+ queryStr += arg["responseStr"]
900
+ queryStr += ") {\n" + renderArgsAndFields("", variablesObj, operation, operation["type"]["definition"], " ") + " }"
901
+ queryStr += indent + "\n}";
902
+ body = {
903
+ "query":queryStr.replace("\n", " ").replace("\t", " ").replace(" ", " ").replace(" ", " ").replace(" ", " "),
904
+ "variables":variablesObj,
905
+ "operationName":renderCamelCase(".".join(operationAry)),
906
+ }
907
+ return body
908
+
909
+ def renderCamelCase(pathStr):
910
+ str = "";
911
+ pathAry = pathStr.split(".")
912
+ for i, path in enumerate(pathAry):
913
+ if i == 0:
914
+ str += path
915
+ else:
916
+ str += path[0].upper() + path[1:]
917
+ return str
918
+
919
+ def renderArgsAndFields(responseArgStr, variablesObj, curOperation, definition, indent):
920
+ for fieldName in definition['fields']:
921
+ field = definition['fields'][fieldName]
922
+ field_name = field['alias'] if 'alias' in field else field['name']
923
+ responseArgStr += indent + field_name
924
+ if field.get("args") and not isinstance(field['args'], list):
925
+ if (len(list(field['args'].keys()))>0):
926
+ argsPresent = False
927
+ argStr = " ( "
928
+ for argName in field['args']:
929
+ arg = field['args'][argName]
930
+ if arg["varName"] in variablesObj:
931
+ argStr += arg['responseStr'] + " "
932
+ argsPresent = True
933
+ argStr += ") "
934
+ if argsPresent==True:
935
+ responseArgStr += argStr
936
+ if field.get("type") and field['type'].get('definition') and field['type']['definition']['fields'] is not None:
937
+ responseArgStr += " {\n"
938
+ for subfieldIndex in field['type']['definition']['fields']:
939
+ subfield = field['type']['definition']['fields'][subfieldIndex]
940
+ subfield_name = subfield['alias'] if 'alias' in subfield else subfield['name']
941
+ responseArgStr += indent + " " + subfield_name
942
+ if subfield.get("args") and len(list(subfield["args"].keys()))>0:
943
+ argsPresent = False
944
+ subArgStr = " ( "
945
+ for argName in subfield['args']:
946
+ arg = subfield['args'][argName]
947
+ if arg["varName"] in variablesObj:
948
+ argsPresent = True
949
+ subArgStr += arg['responseStr'] + " "
950
+ subArgStr += " )"
951
+ if argsPresent==True:
952
+ responseArgStr += subArgStr
953
+ if subfield.get("type") and subfield['type'].get("definition") and (subfield['type']['definition'].get("fields") or subfield['type']['definition'].get('inputFields')):
954
+ responseArgStr += " {\n"
955
+ responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, subfield['type']['definition'], indent + " ")
956
+ if subfield['type']['definition'].get('possibleTypes'):
957
+ for possibleTypeName in subfield['type']['definition']['possibleTypes']:
958
+ possibleType = subfield['type']['definition']['possibleTypes'][possibleTypeName]
959
+ responseArgStr += indent + " ... on " + possibleType['name'] + " {\n"
960
+ if possibleType.get('fields') or possibleType.get('inputFields'):
961
+ responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, possibleType, indent + " ")
962
+ responseArgStr += indent + " }\n"
963
+ responseArgStr += indent + " }"
964
+ elif subfield.get('type') and subfield['type'].get('definition') and subfield['type']['definition'].get('possibleTypes'):
965
+ responseArgStr += " {\n"
966
+ responseArgStr += indent + " __typename\n"
967
+ for possibleTypeName in subfield['type']['definition']['possibleTypes']:
968
+ possibleType = subfield['type']['definition']['possibleTypes'][possibleTypeName]
969
+ responseArgStr += indent + " ... on " + possibleType['name'] + " {\n"
970
+ if possibleType.get('fields') or possibleType.get('inputFields'):
971
+ responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, possibleType, indent + " ")
972
+ responseArgStr += indent + " }\n"
973
+ responseArgStr += indent + " }\n"
974
+ responseArgStr += "\n"
975
+ if field['type']['definition'].get('possibleTypes'):
976
+ for possibleTypeName in field['type']['definition']['possibleTypes']:
977
+ possibleType = field['type']['definition']['possibleTypes'][possibleTypeName]
978
+ responseArgStr += indent + " ... on " + possibleType['name'] + " {\n"
979
+ if possibleType.get('fields') or possibleType.get('inputFields'):
980
+ responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, possibleType, indent + " ")
981
+ responseArgStr += indent + " }\n"
982
+ responseArgStr += indent + "}\n"
983
+ if field.get('type') and field['type'].get('definition') and field['type']['definition'].get('inputFields'):
984
+ responseArgStr += " {\n"
985
+ for subfieldName in field['type']['definition'].get('inputFields'):
986
+ subfield = field['type']['definition']['inputFields'][subfieldName]
987
+ subfield_name = subfield['alias'] if 'alias' in subfield else subfield['name']
988
+ responseArgStr += indent + " " + subfield_name
989
+ if subfield.get('type') and subfield['type'].get('definition') and (subfield['type']['definition'].get('fields') or subfield['type']['definition'].get('inputFields')):
990
+ responseArgStr += " {\n"
991
+ responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, subfield['type']['definition'], indent + " ")
992
+ responseArgStr += indent + " }\n"
993
+ if field['type']['definition'].get('possibleTypes'):
994
+ for possibleTypeName in field['type']['definition']['possibleTypes']:
995
+ possibleType = field['type']['definition']['possibleTypes'][possibleTypeName]
996
+ responseArgStr += indent + "... on " + possibleType['name'] + " {\n"
997
+ if possibleType.get('fields') or possibleType.get('inputFields'):
998
+ responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, possibleType, indent + " ")
999
+ responseArgStr += indent + " }\n"
1000
+ responseArgStr += indent + "}\n"
1001
+ responseArgStr += "\n"
1002
+ return responseArgStr