catocli 2.0.3__py3-none-any.whl → 2.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.
- build/lib/catocli/Utils/clidriver.py +268 -0
- build/lib/catocli/Utils/profile_manager.py +188 -0
- build/lib/catocli/Utils/version_checker.py +192 -0
- build/lib/catocli/__init__.py +2 -0
- build/lib/catocli/__main__.py +12 -0
- build/lib/catocli/parsers/configure/__init__.py +115 -0
- build/lib/catocli/parsers/configure/configure.py +307 -0
- build/lib/catocli/parsers/custom/__init__.py +57 -0
- build/lib/catocli/parsers/custom/customLib.py +561 -0
- build/lib/catocli/parsers/custom/export_rules/__init__.py +42 -0
- build/lib/catocli/parsers/custom/export_rules/export_rules.py +234 -0
- build/lib/catocli/parsers/custom/export_sites/__init__.py +21 -0
- build/lib/catocli/parsers/custom/export_sites/export_sites.py +372 -0
- build/lib/catocli/parsers/custom/import_rules_to_tf/__init__.py +58 -0
- build/lib/catocli/parsers/custom/import_rules_to_tf/import_rules_to_tf.py +451 -0
- build/lib/catocli/parsers/custom/import_sites_to_tf/__init__.py +45 -0
- build/lib/catocli/parsers/custom/import_sites_to_tf/import_sites_to_tf.py +891 -0
- build/lib/catocli/parsers/mutation_accountManagement/__init__.py +48 -0
- build/lib/catocli/parsers/mutation_admin/__init__.py +48 -0
- build/lib/catocli/parsers/mutation_container/__init__.py +138 -0
- build/lib/catocli/parsers/mutation_hardware/__init__.py +22 -0
- build/lib/catocli/parsers/mutation_policy/__init__.py +1305 -0
- build/lib/catocli/parsers/mutation_sandbox/__init__.py +35 -0
- build/lib/catocli/parsers/mutation_site/__init__.py +373 -0
- build/lib/catocli/parsers/mutation_sites/__init__.py +373 -0
- build/lib/catocli/parsers/mutation_xdr/__init__.py +48 -0
- build/lib/catocli/parsers/parserApiClient.py +513 -0
- build/lib/catocli/parsers/query_accountBySubdomain/__init__.py +16 -0
- build/lib/catocli/parsers/query_accountManagement/__init__.py +16 -0
- build/lib/catocli/parsers/query_accountMetrics/__init__.py +16 -0
- build/lib/catocli/parsers/query_accountRoles/__init__.py +16 -0
- build/lib/catocli/parsers/query_accountSnapshot/__init__.py +16 -0
- build/lib/catocli/parsers/query_admin/__init__.py +16 -0
- build/lib/catocli/parsers/query_admins/__init__.py +16 -0
- build/lib/catocli/parsers/query_appStats/__init__.py +16 -0
- build/lib/catocli/parsers/query_appStatsTimeSeries/__init__.py +16 -0
- build/lib/catocli/parsers/query_auditFeed/__init__.py +16 -0
- build/lib/catocli/parsers/query_catalogs/__init__.py +16 -0
- build/lib/catocli/parsers/query_container/__init__.py +16 -0
- build/lib/catocli/parsers/query_devices/__init__.py +16 -0
- build/lib/catocli/parsers/query_entityLookup/__init__.py +16 -0
- build/lib/catocli/parsers/query_events/__init__.py +16 -0
- build/lib/catocli/parsers/query_eventsFeed/__init__.py +16 -0
- build/lib/catocli/parsers/query_eventsTimeSeries/__init__.py +16 -0
- build/lib/catocli/parsers/query_hardware/__init__.py +16 -0
- build/lib/catocli/parsers/query_hardwareManagement/__init__.py +16 -0
- build/lib/catocli/parsers/query_licensing/__init__.py +16 -0
- build/lib/catocli/parsers/query_policy/__init__.py +161 -0
- build/lib/catocli/parsers/query_sandbox/__init__.py +16 -0
- build/lib/catocli/parsers/query_site/__init__.py +100 -0
- build/lib/catocli/parsers/query_siteLocation/__init__.py +13 -0
- build/lib/catocli/parsers/query_subDomains/__init__.py +16 -0
- build/lib/catocli/parsers/query_xdr/__init__.py +35 -0
- build/lib/catocli/parsers/raw/__init__.py +12 -0
- build/lib/graphql_client/__init__.py +11 -0
- build/lib/graphql_client/api/__init__.py +3 -0
- build/lib/graphql_client/api/call_api.py +84 -0
- build/lib/graphql_client/api_client.py +192 -0
- build/lib/graphql_client/api_client_types.py +409 -0
- build/lib/graphql_client/configuration.py +232 -0
- build/lib/graphql_client/models/__init__.py +13 -0
- build/lib/graphql_client/models/no_schema.py +71 -0
- build/lib/schema/catolib.py +1141 -0
- build/lib/schema/importSchema.py +60 -0
- build/lib/schema/remove_policyid.py +89 -0
- build/lib/schema/remove_policyid_mutations.py +89 -0
- build/lib/scripts/catolib.py +62 -0
- build/lib/scripts/export_if_rules_to_json.py +188 -0
- build/lib/scripts/export_wf_rules_to_json.py +111 -0
- build/lib/scripts/import_wf_rules_to_tfstate.py +331 -0
- build/lib/vendor/certifi/__init__.py +4 -0
- build/lib/vendor/certifi/__main__.py +12 -0
- build/lib/vendor/certifi/core.py +114 -0
- build/lib/vendor/certifi/py.typed +0 -0
- build/lib/vendor/six.py +998 -0
- build/lib/vendor/urllib3/__init__.py +211 -0
- build/lib/vendor/urllib3/_base_connection.py +172 -0
- build/lib/vendor/urllib3/_collections.py +483 -0
- build/lib/vendor/urllib3/_request_methods.py +278 -0
- build/lib/vendor/urllib3/_version.py +16 -0
- build/lib/vendor/urllib3/connection.py +1033 -0
- build/lib/vendor/urllib3/connectionpool.py +1182 -0
- build/lib/vendor/urllib3/contrib/__init__.py +0 -0
- build/lib/vendor/urllib3/contrib/emscripten/__init__.py +18 -0
- build/lib/vendor/urllib3/contrib/emscripten/connection.py +254 -0
- build/lib/vendor/urllib3/contrib/emscripten/fetch.py +418 -0
- build/lib/vendor/urllib3/contrib/emscripten/request.py +22 -0
- build/lib/vendor/urllib3/contrib/emscripten/response.py +285 -0
- build/lib/vendor/urllib3/contrib/pyopenssl.py +552 -0
- build/lib/vendor/urllib3/contrib/socks.py +228 -0
- build/lib/vendor/urllib3/exceptions.py +321 -0
- build/lib/vendor/urllib3/fields.py +341 -0
- build/lib/vendor/urllib3/filepost.py +89 -0
- build/lib/vendor/urllib3/http2/__init__.py +53 -0
- build/lib/vendor/urllib3/http2/connection.py +356 -0
- build/lib/vendor/urllib3/http2/probe.py +87 -0
- build/lib/vendor/urllib3/poolmanager.py +637 -0
- build/lib/vendor/urllib3/py.typed +2 -0
- build/lib/vendor/urllib3/response.py +1265 -0
- build/lib/vendor/urllib3/util/__init__.py +42 -0
- build/lib/vendor/urllib3/util/connection.py +137 -0
- build/lib/vendor/urllib3/util/proxy.py +43 -0
- build/lib/vendor/urllib3/util/request.py +256 -0
- build/lib/vendor/urllib3/util/response.py +101 -0
- build/lib/vendor/urllib3/util/retry.py +533 -0
- build/lib/vendor/urllib3/util/ssl_.py +513 -0
- build/lib/vendor/urllib3/util/ssl_match_hostname.py +159 -0
- build/lib/vendor/urllib3/util/ssltransport.py +276 -0
- build/lib/vendor/urllib3/util/timeout.py +275 -0
- build/lib/vendor/urllib3/util/url.py +471 -0
- build/lib/vendor/urllib3/util/util.py +42 -0
- build/lib/vendor/urllib3/util/wait.py +124 -0
- catocli/Utils/clidriver.py +1 -4
- catocli/__init__.py +1 -1
- catocli/parsers/custom/export_rules/__init__.py +2 -0
- catocli/parsers/custom/export_rules/export_rules.py +29 -5
- catocli/parsers/custom/export_sites/__init__.py +1 -0
- catocli/parsers/custom/export_sites/export_sites.py +10 -3
- catocli/parsers/mutation_container/__init__.py +116 -0
- catocli/parsers/mutation_container_fqdn/README.md +7 -0
- catocli/parsers/mutation_container_fqdn_addValues/README.md +17 -0
- catocli/parsers/mutation_container_fqdn_createFromFile/README.md +17 -0
- catocli/parsers/mutation_container_fqdn_removeValues/README.md +17 -0
- catocli/parsers/mutation_container_fqdn_updateFromFile/README.md +17 -0
- catocli/parsers/mutation_container_ipAddressRange/README.md +7 -0
- catocli/parsers/mutation_container_ipAddressRange_addValues/README.md +17 -0
- catocli/parsers/mutation_container_ipAddressRange_createFromFile/README.md +17 -0
- catocli/parsers/mutation_container_ipAddressRange_removeValues/README.md +17 -0
- catocli/parsers/mutation_container_ipAddressRange_updateFromFile/README.md +17 -0
- catocli/parsers/mutation_policy_internetFirewall_addRule/README.md +1 -1
- catocli/parsers/mutation_policy_internetFirewall_updateRule/README.md +1 -1
- catocli/parsers/mutation_policy_wanFirewall_addRule/README.md +1 -1
- catocli/parsers/mutation_policy_wanFirewall_updateRule/README.md +1 -1
- catocli/parsers/parserApiClient.py +183 -7
- catocli/parsers/query_policy/README.md +0 -17
- catocli/parsers/query_policy/__init__.py +153 -8
- catocli/parsers/query_policy_appTenantRestriction/README.md +7 -0
- catocli/parsers/query_policy_appTenantRestriction_policy/README.md +17 -0
- catocli/parsers/query_policy_dynamicIpAllocation/README.md +7 -0
- catocli/parsers/query_policy_dynamicIpAllocation_policy/README.md +17 -0
- catocli/parsers/query_policy_internetFirewall/README.md +7 -0
- catocli/parsers/query_policy_internetFirewall_policy/README.md +17 -0
- catocli/parsers/query_policy_remotePortFwd/README.md +7 -0
- catocli/parsers/query_policy_remotePortFwd_policy/README.md +17 -0
- catocli/parsers/query_policy_socketLan/README.md +7 -0
- catocli/parsers/query_policy_socketLan_policy/README.md +17 -0
- catocli/parsers/query_policy_terminalServer/README.md +7 -0
- catocli/parsers/query_policy_terminalServer_policy/README.md +17 -0
- catocli/parsers/query_policy_wanFirewall/README.md +7 -0
- catocli/parsers/query_policy_wanFirewall_policy/README.md +17 -0
- catocli/parsers/query_policy_wanNetwork/README.md +7 -0
- catocli/parsers/query_policy_wanNetwork_policy/README.md +17 -0
- catocli/parsers/query_site/README.md +0 -16
- catocli/parsers/query_site/__init__.py +92 -8
- catocli/parsers/query_site_availableVersionList/README.md +17 -0
- catocli/parsers/query_site_bgpPeer/README.md +17 -0
- catocli/parsers/query_site_bgpPeerList/README.md +17 -0
- catocli/parsers/query_site_cloudInterconnectConnectionConnectivity/README.md +17 -0
- catocli/parsers/query_site_cloudInterconnectPhysicalConnection/README.md +17 -0
- catocli/parsers/query_site_cloudInterconnectPhysicalConnectionId/README.md +17 -0
- catocli/parsers/query_site_siteBgpStatus/README.md +17 -0
- {catocli-2.0.3.dist-info → catocli-2.0.5.dist-info}/METADATA +1 -1
- {catocli-2.0.3.dist-info → catocli-2.0.5.dist-info}/RECORD +291 -121
- {catocli-2.0.3.dist-info → catocli-2.0.5.dist-info}/top_level.txt +1 -0
- graphql_client/api/call_api.py +4 -3
- models/mutation.container.fqdn.addValues.json +866 -0
- models/mutation.container.fqdn.createFromFile.json +819 -0
- models/mutation.container.fqdn.removeValues.json +866 -0
- models/mutation.container.fqdn.updateFromFile.json +1045 -0
- models/mutation.container.ipAddressRange.addValues.json +1020 -0
- models/mutation.container.ipAddressRange.createFromFile.json +819 -0
- models/mutation.container.ipAddressRange.removeValues.json +1020 -0
- models/mutation.container.ipAddressRange.updateFromFile.json +1045 -0
- models/mutation.policy.appTenantRestriction.addRule.json +8 -8
- models/mutation.policy.appTenantRestriction.addSection.json +1 -1
- models/mutation.policy.appTenantRestriction.createPolicyRevision.json +2 -2
- models/mutation.policy.appTenantRestriction.discardPolicyRevision.json +2 -2
- models/mutation.policy.appTenantRestriction.moveRule.json +2 -2
- models/mutation.policy.appTenantRestriction.moveSection.json +1 -1
- models/mutation.policy.appTenantRestriction.publishPolicyRevision.json +2 -2
- models/mutation.policy.appTenantRestriction.removeRule.json +2 -2
- models/mutation.policy.appTenantRestriction.removeSection.json +1 -1
- models/mutation.policy.appTenantRestriction.updatePolicy.json +2 -2
- models/mutation.policy.appTenantRestriction.updateRule.json +8 -8
- models/mutation.policy.appTenantRestriction.updateSection.json +1 -1
- models/mutation.policy.dynamicIpAllocation.addRule.json +1 -1
- models/mutation.policy.dynamicIpAllocation.addSection.json +1 -1
- models/mutation.policy.dynamicIpAllocation.createPolicyRevision.json +1 -1
- models/mutation.policy.dynamicIpAllocation.discardPolicyRevision.json +1 -1
- models/mutation.policy.dynamicIpAllocation.moveRule.json +1 -1
- models/mutation.policy.dynamicIpAllocation.moveSection.json +1 -1
- models/mutation.policy.dynamicIpAllocation.publishPolicyRevision.json +1 -1
- models/mutation.policy.dynamicIpAllocation.removeRule.json +1 -1
- models/mutation.policy.dynamicIpAllocation.removeSection.json +1 -1
- models/mutation.policy.dynamicIpAllocation.updatePolicy.json +1 -1
- models/mutation.policy.dynamicIpAllocation.updateRule.json +1 -1
- models/mutation.policy.dynamicIpAllocation.updateSection.json +1 -1
- models/mutation.policy.internetFirewall.addRule.json +502 -55
- models/mutation.policy.internetFirewall.addSection.json +1 -1
- models/mutation.policy.internetFirewall.createPolicyRevision.json +127 -10
- models/mutation.policy.internetFirewall.discardPolicyRevision.json +127 -10
- models/mutation.policy.internetFirewall.moveRule.json +127 -10
- models/mutation.policy.internetFirewall.moveSection.json +1 -1
- models/mutation.policy.internetFirewall.publishPolicyRevision.json +127 -10
- models/mutation.policy.internetFirewall.removeRule.json +127 -10
- models/mutation.policy.internetFirewall.removeSection.json +1 -1
- models/mutation.policy.internetFirewall.updatePolicy.json +127 -10
- models/mutation.policy.internetFirewall.updateRule.json +493 -55
- models/mutation.policy.internetFirewall.updateSection.json +1 -1
- models/mutation.policy.remotePortFwd.addRule.json +5 -5
- models/mutation.policy.remotePortFwd.addSection.json +1 -1
- models/mutation.policy.remotePortFwd.createPolicyRevision.json +2 -2
- models/mutation.policy.remotePortFwd.discardPolicyRevision.json +2 -2
- models/mutation.policy.remotePortFwd.moveRule.json +2 -2
- models/mutation.policy.remotePortFwd.moveSection.json +1 -1
- models/mutation.policy.remotePortFwd.publishPolicyRevision.json +2 -2
- models/mutation.policy.remotePortFwd.removeRule.json +2 -2
- models/mutation.policy.remotePortFwd.removeSection.json +1 -1
- models/mutation.policy.remotePortFwd.updatePolicy.json +2 -2
- models/mutation.policy.remotePortFwd.updateRule.json +5 -5
- models/mutation.policy.remotePortFwd.updateSection.json +1 -1
- models/mutation.policy.socketLan.addRule.json +16 -16
- models/mutation.policy.socketLan.addSection.json +1 -1
- models/mutation.policy.socketLan.createPolicyRevision.json +4 -4
- models/mutation.policy.socketLan.discardPolicyRevision.json +4 -4
- models/mutation.policy.socketLan.moveRule.json +4 -4
- models/mutation.policy.socketLan.moveSection.json +1 -1
- models/mutation.policy.socketLan.publishPolicyRevision.json +4 -4
- models/mutation.policy.socketLan.removeRule.json +4 -4
- models/mutation.policy.socketLan.removeSection.json +1 -1
- models/mutation.policy.socketLan.updatePolicy.json +4 -4
- models/mutation.policy.socketLan.updateRule.json +16 -16
- models/mutation.policy.socketLan.updateSection.json +1 -1
- models/mutation.policy.terminalServer.addRule.json +1 -1
- models/mutation.policy.terminalServer.addSection.json +1 -1
- models/mutation.policy.terminalServer.createPolicyRevision.json +1 -1
- models/mutation.policy.terminalServer.discardPolicyRevision.json +1 -1
- models/mutation.policy.terminalServer.moveRule.json +1 -1
- models/mutation.policy.terminalServer.moveSection.json +1 -1
- models/mutation.policy.terminalServer.publishPolicyRevision.json +1 -1
- models/mutation.policy.terminalServer.removeRule.json +1 -1
- models/mutation.policy.terminalServer.removeSection.json +1 -1
- models/mutation.policy.terminalServer.updatePolicy.json +1 -1
- models/mutation.policy.terminalServer.updateRule.json +1 -1
- models/mutation.policy.terminalServer.updateSection.json +1 -1
- models/mutation.policy.wanFirewall.addRule.json +500 -53
- models/mutation.policy.wanFirewall.addSection.json +1 -1
- models/mutation.policy.wanFirewall.createPolicyRevision.json +128 -11
- models/mutation.policy.wanFirewall.discardPolicyRevision.json +128 -11
- models/mutation.policy.wanFirewall.moveRule.json +128 -11
- models/mutation.policy.wanFirewall.moveSection.json +1 -1
- models/mutation.policy.wanFirewall.publishPolicyRevision.json +128 -11
- models/mutation.policy.wanFirewall.removeRule.json +128 -11
- models/mutation.policy.wanFirewall.removeSection.json +1 -1
- models/mutation.policy.wanFirewall.updatePolicy.json +128 -11
- models/mutation.policy.wanFirewall.updateRule.json +491 -53
- models/mutation.policy.wanFirewall.updateSection.json +1 -1
- models/mutation.policy.wanNetwork.addRule.json +13 -13
- models/mutation.policy.wanNetwork.addSection.json +1 -1
- models/mutation.policy.wanNetwork.createPolicyRevision.json +1 -1
- models/mutation.policy.wanNetwork.discardPolicyRevision.json +1 -1
- models/mutation.policy.wanNetwork.moveRule.json +1 -1
- models/mutation.policy.wanNetwork.moveSection.json +1 -1
- models/mutation.policy.wanNetwork.publishPolicyRevision.json +1 -1
- models/mutation.policy.wanNetwork.removeRule.json +1 -1
- models/mutation.policy.wanNetwork.removeSection.json +1 -1
- models/mutation.policy.wanNetwork.updatePolicy.json +1 -1
- models/mutation.policy.wanNetwork.updateRule.json +13 -13
- models/mutation.policy.wanNetwork.updateSection.json +1 -1
- models/query.policy.appTenantRestriction.policy.json +3086 -0
- models/query.policy.dynamicIpAllocation.policy.json +1934 -0
- models/query.policy.internetFirewall.policy.json +7833 -0
- models/query.policy.json +233 -0
- models/query.policy.remotePortFwd.policy.json +2387 -0
- models/query.policy.socketLan.policy.json +7140 -0
- models/query.policy.terminalServer.policy.json +1632 -0
- models/query.policy.wanFirewall.policy.json +9212 -0
- models/query.policy.wanNetwork.policy.json +8010 -0
- models/query.site.availableVersionList.json +365 -0
- models/query.site.bgpPeer.json +1917 -0
- models/query.site.bgpPeerList.json +2076 -0
- models/query.site.cloudInterconnectConnectionConnectivity.json +298 -0
- models/query.site.cloudInterconnectPhysicalConnection.json +728 -0
- models/query.site.cloudInterconnectPhysicalConnectionId.json +660 -0
- models/query.site.siteBgpStatus.json +869 -0
- schema/catolib.py +13 -6
- schema/remove_policyid.py +89 -0
- schema/remove_policyid_mutations.py +89 -0
- {catocli-2.0.3.dist-info → catocli-2.0.5.dist-info}/LICENSE +0 -0
- {catocli-2.0.3.dist-info → catocli-2.0.5.dist-info}/WHEEL +0 -0
- {catocli-2.0.3.dist-info → catocli-2.0.5.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,1141 @@
|
|
|
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
|
+
if "CATO_ACCOUNT_ID" not in os.environ:
|
|
36
|
+
print("Missing authentication, please set the CATO_ACCOUNT_ID environment variable with your api key.")
|
|
37
|
+
exit()
|
|
38
|
+
|
|
39
|
+
# Process options
|
|
40
|
+
parser = OptionParser()
|
|
41
|
+
parser.add_option("-P", dest="prettify", action="store_true", help="Prettify output")
|
|
42
|
+
parser.add_option("-p", dest="print_entities", action="store_true", help="Print entity records")
|
|
43
|
+
parser.add_option("-v", dest="verbose", action="store_true", help="Print debug info")
|
|
44
|
+
(options, args) = parser.parse_args()
|
|
45
|
+
options.api_key = os.getenv("CATO_TOKEN")
|
|
46
|
+
if options.verbose:
|
|
47
|
+
logging.getLogger().setLevel(logging.DEBUG)
|
|
48
|
+
else:
|
|
49
|
+
logging.getLogger().setLevel(logging.INFO)
|
|
50
|
+
return options
|
|
51
|
+
|
|
52
|
+
def loadJSON(file):
|
|
53
|
+
CONFIG = {}
|
|
54
|
+
try:
|
|
55
|
+
with open(file, 'r') as data:
|
|
56
|
+
CONFIG = json.load(data)
|
|
57
|
+
logging.warning("Loaded "+file+" data")
|
|
58
|
+
return CONFIG
|
|
59
|
+
except:
|
|
60
|
+
logging.warning("File \""+file+"\" not found.")
|
|
61
|
+
exit()
|
|
62
|
+
|
|
63
|
+
def writeFile(fileName, data):
|
|
64
|
+
open(fileName, 'w+').close()
|
|
65
|
+
file=open(fileName,"w+")
|
|
66
|
+
file.write(data)
|
|
67
|
+
|
|
68
|
+
def openFile(fileName, readMode="rt"):
|
|
69
|
+
try:
|
|
70
|
+
with open(fileName, readMode) as f:
|
|
71
|
+
fileTxt = f.read()
|
|
72
|
+
f.closed
|
|
73
|
+
return fileTxt
|
|
74
|
+
except:
|
|
75
|
+
print('[ERROR] File path "'+fileName+'" in csv not found, or script unable to read.')
|
|
76
|
+
exit()
|
|
77
|
+
|
|
78
|
+
############ parsing schema ############
|
|
79
|
+
|
|
80
|
+
def parseSchema(schema):
|
|
81
|
+
# Load settings to get childOperationParent and childOperationObjects configuration
|
|
82
|
+
settings = loadJSON("../settings.json")
|
|
83
|
+
childOperationParent = settings.get("childOperationParent", {})
|
|
84
|
+
childOperationObjects = settings.get("childOperationObjects", {})
|
|
85
|
+
|
|
86
|
+
mutationOperationsTMP = {}
|
|
87
|
+
queryOperationsTMP = {}
|
|
88
|
+
for i, type in enumerate(schema["data"]["__schema"]["types"]):
|
|
89
|
+
if type["kind"] == "ENUM":
|
|
90
|
+
catoApiIntrospection["enums"][type["name"]] = copy.deepcopy(type)
|
|
91
|
+
elif type["kind"] == "SCALAR":
|
|
92
|
+
catoApiIntrospection["scalars"][type["name"]] = copy.deepcopy(type)
|
|
93
|
+
elif type["kind"] == "INPUT_OBJECT":
|
|
94
|
+
catoApiIntrospection["input_objects"][type["name"]] = copy.deepcopy(type)
|
|
95
|
+
elif type["kind"] == "INTERFACE":
|
|
96
|
+
catoApiIntrospection["interfaces"][type["name"]] = copy.deepcopy(type)
|
|
97
|
+
elif type["kind"] == "UNION":
|
|
98
|
+
catoApiIntrospection["unions"][type["name"]] = copy.deepcopy(type)
|
|
99
|
+
elif type["kind"] == "OBJECT":
|
|
100
|
+
if type["name"] == "Query":
|
|
101
|
+
for field in type["fields"]:
|
|
102
|
+
if field["name"] in childOperationParent:
|
|
103
|
+
queryOperationsTMP[field["name"]] = copy.deepcopy(field)
|
|
104
|
+
else:
|
|
105
|
+
catoApiSchema["query"]["query."+field["name"]] = copy.deepcopy(field)
|
|
106
|
+
# catoParserMapping["query"][field["name"]]
|
|
107
|
+
elif type["name"] == "Mutation":
|
|
108
|
+
for field in type["fields"]:
|
|
109
|
+
mutationOperationsTMP[field["name"]] = copy.deepcopy(field)
|
|
110
|
+
else:
|
|
111
|
+
catoApiIntrospection["objects"][type["name"]] = copy.deepcopy(type)
|
|
112
|
+
|
|
113
|
+
for queryType in queryOperationsTMP:
|
|
114
|
+
parentQueryOperationType = copy.deepcopy(queryOperationsTMP[queryType])
|
|
115
|
+
getChildOperations("query", parentQueryOperationType, parentQueryOperationType, "query." + queryType, childOperationObjects)
|
|
116
|
+
|
|
117
|
+
for mutationType in mutationOperationsTMP:
|
|
118
|
+
parentMutationOperationType = copy.deepcopy(mutationOperationsTMP[mutationType])
|
|
119
|
+
getChildOperations("mutation", parentMutationOperationType, parentMutationOperationType, "mutation." + mutationType, childOperationObjects)
|
|
120
|
+
|
|
121
|
+
for operationType in catoApiSchema:
|
|
122
|
+
for operationName in catoApiSchema[operationType]:
|
|
123
|
+
# if operationName=="query.xdr.stories":
|
|
124
|
+
childOperations = catoApiSchema[operationType][operationName]["childOperations"].keys() if "childOperations" in catoApiSchema[operationType][operationName] and catoApiSchema[operationType][operationName]!=None else []
|
|
125
|
+
parsedOperation = parseOperation(catoApiSchema[operationType][operationName],childOperations)
|
|
126
|
+
parsedOperation = getOperationArgs(parsedOperation["type"]["definition"],parsedOperation)
|
|
127
|
+
parsedOperation["path"] = operationName
|
|
128
|
+
for argName in parsedOperation["args"]:
|
|
129
|
+
arg = parsedOperation["args"][argName]
|
|
130
|
+
parsedOperation["operationArgs"][arg["varName"]] = arg
|
|
131
|
+
parsedOperation["variablesPayload"] = generateExampleVariables(parsedOperation)
|
|
132
|
+
writeFile("../models/"+operationName+".json",json.dumps(parsedOperation, indent=4, sort_keys=True))
|
|
133
|
+
writeFile("../queryPayloads/"+operationName+".json",json.dumps(generateGraphqlPayload(parsedOperation["variablesPayload"],parsedOperation,operationName),indent=4,sort_keys=True))
|
|
134
|
+
payload = generateGraphqlPayload(parsedOperation["variablesPayload"],parsedOperation,operationName)
|
|
135
|
+
writeFile("../queryPayloads/"+operationName+".txt",payload["query"])
|
|
136
|
+
|
|
137
|
+
def getChildOperations(operationType, curType, parentType, parentPath, childOperationObjects):
|
|
138
|
+
# Parse fields for nested args to map out all child operations
|
|
139
|
+
# This will separate fields like stories and story for query.xdr,
|
|
140
|
+
# and all fields which are actually sub operations from mutation.internetFirewall, etc
|
|
141
|
+
if "childOperations" not in parentType:
|
|
142
|
+
parentType["childOperations"] = {}
|
|
143
|
+
curOfType = None
|
|
144
|
+
if "kind" in curType:
|
|
145
|
+
curOfType = copy.deepcopy(catoApiIntrospection[curType["kind"].lower() + "s"][curType["name"]])
|
|
146
|
+
elif "type" in curType and curType["type"]["ofType"]==None:
|
|
147
|
+
curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["kind"].lower() + "s"][curType["type"]["name"]])
|
|
148
|
+
elif "type" in curType and curType["type"]["ofType"]["ofType"]==None:
|
|
149
|
+
curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["ofType"]["kind"].lower() + "s"][curType["type"]["ofType"]["name"]])
|
|
150
|
+
elif "type" in curType and curType["type"]["ofType"]["ofType"]["ofType"]==None:
|
|
151
|
+
curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["ofType"]["ofType"]["kind"].lower() + "s"][curType["type"]["ofType"]["ofType"]["name"]])
|
|
152
|
+
elif "type" in curType and curType["type"]["ofType"]["ofType"]["ofType"]["ofType"]==None:
|
|
153
|
+
curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["ofType"]["ofType"]["ofType"]["kind"].lower() + "s"][curType["type"]["ofType"]["ofType"]["ofType"]["name"]])
|
|
154
|
+
else:
|
|
155
|
+
curOfType = copy.deepcopy(catoApiIntrospection[curType["type"]["ofType"]["ofType"]["ofType"]["ofType"]["kind"].lower() + "s"][curType["type"]["ofType"]["ofType"]["ofType"]["ofType"]["name"]])
|
|
156
|
+
hasChildren = False
|
|
157
|
+
|
|
158
|
+
if "fields" in curOfType and curOfType["fields"] != None:
|
|
159
|
+
parentFields = []
|
|
160
|
+
for field in curOfType["fields"]:
|
|
161
|
+
curFieldObject = copy.deepcopy(field)
|
|
162
|
+
if (("args" in curFieldObject and len(curFieldObject["args"])>0) or
|
|
163
|
+
(curFieldObject["name"] in childOperationObjects) or
|
|
164
|
+
(curOfType["name"] in childOperationObjects)):
|
|
165
|
+
hasChildren = True
|
|
166
|
+
curParentType = copy.deepcopy(parentType)
|
|
167
|
+
curFieldObject["args"] = getNestedArgDefinitions(curFieldObject["args"], curFieldObject["name"],None,None)
|
|
168
|
+
curParentType["childOperations"][curFieldObject["name"]] = curFieldObject
|
|
169
|
+
getChildOperations(operationType, curFieldObject, curParentType, parentPath + "." + curFieldObject["name"], childOperationObjects)
|
|
170
|
+
if not hasChildren:
|
|
171
|
+
catoApiSchema[operationType][parentPath] = parentType
|
|
172
|
+
|
|
173
|
+
def getNestedArgDefinitions(argsAry, parentParamPath, childOperations, parentFields):
|
|
174
|
+
newArgsList = {}
|
|
175
|
+
for arg in argsAry:
|
|
176
|
+
curParamPath = renderCamelCase(arg["name"]) if (parentParamPath == None or parentParamPath == "") else parentParamPath.replace("___",".") + "." + renderCamelCase(arg["name"])
|
|
177
|
+
if "path" in arg and '.' not in arg["path"]:
|
|
178
|
+
arg["child"] = True
|
|
179
|
+
arg["parent"] = arg["path"]
|
|
180
|
+
arg["type"] = getOfType(arg["type"], { "non_null": False, "kind": [], "name": None }, curParamPath, childOperations, parentFields)
|
|
181
|
+
arg["path"] = curParamPath
|
|
182
|
+
arg["id_str"] = curParamPath.replace(".","___")
|
|
183
|
+
if isinstance(arg["type"]["kind"], list):
|
|
184
|
+
arg["required"] = True if arg["type"]["kind"][0] == "NON_NULL" else False
|
|
185
|
+
else:
|
|
186
|
+
arg["required"] = True if arg["type"]["kind"] == "NON_NULL" else False
|
|
187
|
+
required1 = "!" if arg["required"] else ""
|
|
188
|
+
required2 = "!" if "NON_NULL" in arg["type"]["kind"][1:] else ""
|
|
189
|
+
if "SCALAR" in arg["type"]["kind"] or "ENUM" in arg["type"]["kind"]:
|
|
190
|
+
arg["varName"] = renderCamelCase(arg["name"])
|
|
191
|
+
# arg["id_str"] = arg["varName"]
|
|
192
|
+
else:
|
|
193
|
+
arg["varName"] = renderCamelCase(arg["type"]["name"])
|
|
194
|
+
arg["responseStr"] = arg["name"] + ":$" + arg["varName"] + " "
|
|
195
|
+
if "LIST" in arg["type"]["kind"]:
|
|
196
|
+
arg["requestStr"] = "$" + arg["varName"] + ":" + "[" + arg["type"]["name"] + required2 + "]" + required1 + " "
|
|
197
|
+
else:
|
|
198
|
+
arg["requestStr"] = "$" + arg["varName"] + ":" + arg["type"]["name"] + required1 + " "
|
|
199
|
+
newArgsList[arg["id_str"]] = arg
|
|
200
|
+
# print("getNestedArgDefinitions()",newArgsList.keys())
|
|
201
|
+
return newArgsList
|
|
202
|
+
|
|
203
|
+
def getOfType(curType, ofType, parentParamPath, childOperations, parentFields):
|
|
204
|
+
ofType["kind"].append(copy.deepcopy(curType["kind"]))
|
|
205
|
+
curParamPath = "" if (parentParamPath == None) else parentParamPath + "___"
|
|
206
|
+
if curType["ofType"] != None:
|
|
207
|
+
ofType = getOfType(copy.deepcopy(curType["ofType"]), ofType, parentParamPath,childOperations,parentFields)
|
|
208
|
+
else:
|
|
209
|
+
ofType["name"] = curType["name"]
|
|
210
|
+
parentFields = []
|
|
211
|
+
if "definition" in ofType and "fields" in ofType["definition"] and ofType["definition"]["fields"]!=None:
|
|
212
|
+
for fieldName in ofType["definition"]["fields"]:
|
|
213
|
+
field = ofType["definition"]["fields"][fieldName]
|
|
214
|
+
parentFields.append(field["name"])
|
|
215
|
+
if "INPUT_OBJECT" in ofType["kind"]:
|
|
216
|
+
ofType["indexType"] = "input_object"
|
|
217
|
+
ofType["definition"] = copy.deepcopy(catoApiIntrospection["input_objects"][ofType["name"]])
|
|
218
|
+
if ofType["definition"]["inputFields"] != None:
|
|
219
|
+
ofType["definition"]["inputFields"] = getNestedFieldDefinitions(copy.deepcopy(ofType["definition"]["inputFields"]), curParamPath, childOperations, parentFields)
|
|
220
|
+
elif "UNION" in ofType["kind"]:
|
|
221
|
+
ofType["indexType"] = "interface"
|
|
222
|
+
ofType["definition"] = copy.deepcopy(catoApiIntrospection["unions"][ofType["name"]])
|
|
223
|
+
if ofType["definition"]["possibleTypes"] != None:
|
|
224
|
+
ofType["definition"]["possibleTypes"] = getNestedInterfaceDefinitions(copy.deepcopy(ofType["definition"]["possibleTypes"]), curParamPath,childOperations, parentFields)
|
|
225
|
+
# strip out each nested interface attribute from parent oftype fields,
|
|
226
|
+
# this is to prevent duplicate fields causing the query to fail
|
|
227
|
+
for interfaceName in ofType["definition"]["possibleTypes"]:
|
|
228
|
+
possibleType = ofType["definition"]["possibleTypes"][interfaceName]
|
|
229
|
+
if ofType["definition"]["fields"]!=None:
|
|
230
|
+
for fieldName in ofType["definition"]["fields"]:
|
|
231
|
+
field = ofType["definition"]["fields"][fieldName]
|
|
232
|
+
nestedFieldPath = parentParamPath + interfaceName + "___" + field["name"]
|
|
233
|
+
# aliasLogic
|
|
234
|
+
# if field["name"] in parentFields:
|
|
235
|
+
# field["alias"] = renderCamelCase(interfaceName+"."+field["name"])+": "+field["name"]
|
|
236
|
+
# if "SCALAR" in field["type"]["kind"] or "ENUM" in field["type"]["kind"]:
|
|
237
|
+
# possibleType["fields"][nestedFieldPath]["alias"] = renderCamelCase(interfaceName+"."+field["name"])+": "+field["name"]
|
|
238
|
+
# else:
|
|
239
|
+
# possibleType["fields"][nestedFieldPath]["alias"] = renderCamelCase(field["type"]["name"])+": "+field["name"]
|
|
240
|
+
ofType["definition"]["possibleTypes"][interfaceName] = copy.deepcopy(possibleType)
|
|
241
|
+
elif "OBJECT" in ofType["kind"]:
|
|
242
|
+
ofType["indexType"] = "object"
|
|
243
|
+
ofType["definition"] = copy.deepcopy(catoApiIntrospection["objects"][ofType["name"]])
|
|
244
|
+
if ofType["definition"]["fields"] != None and childOperations!=None:
|
|
245
|
+
ofType["definition"]["fields"] = checkForChildOperation(copy.deepcopy(ofType["definition"]["fields"]),childOperations)
|
|
246
|
+
ofType["definition"]["fields"] = getNestedFieldDefinitions(copy.deepcopy(ofType["definition"]["fields"]), curParamPath,childOperations, parentFields)
|
|
247
|
+
if ofType["definition"]["interfaces"] != None:
|
|
248
|
+
ofType["definition"]["interfaces"] = getNestedInterfaceDefinitions(copy.deepcopy(ofType["definition"]["interfaces"]), curParamPath,childOperations, parentFields)
|
|
249
|
+
elif "INTERFACE" in ofType["kind"]:
|
|
250
|
+
ofType["indexType"] = "interface"
|
|
251
|
+
ofType["definition"] = copy.deepcopy(catoApiIntrospection["interfaces"][ofType["name"]])
|
|
252
|
+
if ofType["definition"]["fields"] != None:
|
|
253
|
+
ofType["definition"]["fields"] = getNestedFieldDefinitions(copy.deepcopy(ofType["definition"]["fields"]), curParamPath, childOperations, parentFields)
|
|
254
|
+
if ofType["definition"]["possibleTypes"] != None:
|
|
255
|
+
ofType["definition"]["possibleTypes"] = getNestedInterfaceDefinitions(copy.deepcopy(ofType["definition"]["possibleTypes"]), curParamPath, childOperations, parentFields)
|
|
256
|
+
for interfaceName in ofType["definition"]["possibleTypes"]:
|
|
257
|
+
possibleType = copy.deepcopy(ofType["definition"]["possibleTypes"][interfaceName])
|
|
258
|
+
for fieldName in ofType["definition"]["fields"]:
|
|
259
|
+
field = ofType["definition"]["fields"][fieldName]
|
|
260
|
+
nestedFieldPath = parentParamPath + interfaceName + "." + field["name"]
|
|
261
|
+
nestedFieldPath = nestedFieldPath.replace(".","___")
|
|
262
|
+
if "args" in field and len(field["args"])>0:
|
|
263
|
+
field["args"] = getNestedArgDefinitions(copy.deepcopy(field["args"]), nestedFieldPath, curParamPath, parentFields)
|
|
264
|
+
# aliasLogic MUST
|
|
265
|
+
# CatoEndpointUser
|
|
266
|
+
if field["name"] in possibleType["fields"] and possibleType["fields"][field["name"]] != None:
|
|
267
|
+
# del possibleType["fields"][field["name"]]
|
|
268
|
+
if "SCALAR" in field["type"]["kind"] or "ENUM" in field["type"]["kind"]:
|
|
269
|
+
possibleType["fields"][field["name"]]["alias"] = renderCamelCase(interfaceName+"."+field["name"])+": "+field["name"]
|
|
270
|
+
else:
|
|
271
|
+
possibleType["fields"][field["name"]]["alias"] = renderCamelCase(field["type"]["name"])+": "+field["name"]
|
|
272
|
+
ofType["definition"]["possibleTypes"][interfaceName] = copy.deepcopy(possibleType)
|
|
273
|
+
if ofType["definition"]["interfaces"] != None:
|
|
274
|
+
ofType["definition"]["interfaces"] = getNestedInterfaceDefinitions(copy.deepcopy(ofType["definition"]["interfaces"]), curParamPath,childOperations, parentFields)
|
|
275
|
+
elif "ENUM" in ofType["kind"]:
|
|
276
|
+
ofType["indexType"] = "enum"
|
|
277
|
+
ofType["definition"] = copy.deepcopy(catoApiIntrospection["enums"][ofType["name"]])
|
|
278
|
+
return ofType
|
|
279
|
+
|
|
280
|
+
def getNestedFieldDefinitions(fieldsAry, parentParamPath,childOperations, parentFields):
|
|
281
|
+
newFieldsList = {}
|
|
282
|
+
for field in fieldsAry:
|
|
283
|
+
if isinstance(field,str):
|
|
284
|
+
field = fieldsAry[field]
|
|
285
|
+
curParamPath = field["name"] if (parentParamPath == None) else (parentParamPath.replace("___",".") + field["name"])
|
|
286
|
+
# curParamPath = field["name"] if (parentParamPath == None) else (parentParamPath + "." + field["name"])
|
|
287
|
+
field["type"] = getOfType(field["type"], { "non_null": False, "kind": [], "name": None }, curParamPath,childOperations, parentFields)
|
|
288
|
+
field["path"] = curParamPath
|
|
289
|
+
field["id_str"] = curParamPath.replace(".","___")
|
|
290
|
+
if isinstance(field["type"]["kind"], list):
|
|
291
|
+
field["required"] = True if field["type"]["kind"][0] == "NON_NULL" else False
|
|
292
|
+
else:
|
|
293
|
+
field["required"] = True if field["type"]["kind"] == "NON_NULL" else False
|
|
294
|
+
required1 = "!" if field["required"] else ""
|
|
295
|
+
required2 = "!" if field["type"]["kind"][1:] == "NON_NULL" else ""
|
|
296
|
+
if "SCALAR" in field["type"]["kind"] or "ENUM" in field["type"]["kind"]:
|
|
297
|
+
field["varName"] = renderCamelCase(field["name"])
|
|
298
|
+
# field["id_str"] = field["varName"]
|
|
299
|
+
else:
|
|
300
|
+
field["varName"] = renderCamelCase(field["type"]["name"])
|
|
301
|
+
field["responseStr"] = field["name"] + ":$" + field["varName"] + " "
|
|
302
|
+
if "LIST" in field["type"]["kind"]:
|
|
303
|
+
field["requestStr"] = "$" + field["varName"] + ":" + "[" + field["type"]["name"] + required2 + "]" + required1 + " "
|
|
304
|
+
else:
|
|
305
|
+
field["requestStr"] = "$" + field["varName"] + ":" + field["type"]["name"] + required1 + " "
|
|
306
|
+
if "args" in field:
|
|
307
|
+
field["args"] = getNestedArgDefinitions(field["args"], field["name"],childOperations, parentFields)
|
|
308
|
+
## aliasLogic must
|
|
309
|
+
if parentFields!=None and field["name"] in parentFields and "SCALAR" not in field["type"]["kind"]:
|
|
310
|
+
# if field["name"]=="records":
|
|
311
|
+
# raise ValueError('A very specific bad thing happened.')
|
|
312
|
+
# print(json.dumps(field,indent=2,sort_keys=True))
|
|
313
|
+
# print(field["path"],parentFields)
|
|
314
|
+
# field["alias"] = renderCamelCase(field["type"]["name"]+"."+field["name"])+": "+field["name"]
|
|
315
|
+
field["alias"] = renderCamelCase(field["type"]["name"]+"."+field["name"])+": "+field["name"]
|
|
316
|
+
if "records.fields" not in field["path"]:
|
|
317
|
+
newFieldsList[field["name"]] = field
|
|
318
|
+
# for (fieldPath in newFieldList) {
|
|
319
|
+
# var field = newFieldList[fieldPath];
|
|
320
|
+
# if (curOperationObj.fieldTypes && field.type.name != null) curOperationObj.fieldTypes[field.type.name] = true;
|
|
321
|
+
# field.type = getOfType(field.type, { non_null: false, kind: [], name: null }, field.path);
|
|
322
|
+
# }
|
|
323
|
+
return newFieldsList
|
|
324
|
+
|
|
325
|
+
def getNestedInterfaceDefinitions(possibleTypesAry, parentParamPath,childOperations, parentFields):
|
|
326
|
+
curInterfaces = {}
|
|
327
|
+
for possibleType in possibleTypesAry:
|
|
328
|
+
if "OBJECT" in possibleType["kind"]:
|
|
329
|
+
curInterfaces[possibleType["name"]] = copy.deepcopy(catoApiIntrospection["objects"][possibleType["name"]])
|
|
330
|
+
# for curInterface in curInterfaces:
|
|
331
|
+
# curParamPath = "" if parentParamPath == None else parentParamPath + curInterface["name"] + "___"
|
|
332
|
+
for curInterfaceName in curInterfaces:
|
|
333
|
+
curInterface = curInterfaces[curInterfaceName]
|
|
334
|
+
curParamPath = "" if parentParamPath == None else parentParamPath + curInterface["name"] + "___"
|
|
335
|
+
if "fields" in curInterface and curInterface["fields"] != None:
|
|
336
|
+
curInterface["fields"] = getNestedFieldDefinitions(copy.deepcopy(curInterface["fields"]), curParamPath,childOperations, parentFields)
|
|
337
|
+
if "inputFields" in curInterface and curInterface["inputFields"] != None:
|
|
338
|
+
curInterface["inputFields"] = getNestedFieldDefinitions(copy.deepcopy(curInterface["inputFields"]), curParamPath,childOperations, parentFields)
|
|
339
|
+
if "interfaces" in curInterface and curInterface["interfaces"] != None:
|
|
340
|
+
curInterface["interfaces"] = getNestedInterfaceDefinitions(copy.deepcopy(curInterface["interfaces"]), curParamPath,childOperations, parentFields)
|
|
341
|
+
if "possibleTypes" in curInterface and curInterface["possibleTypes"] != None:
|
|
342
|
+
curInterface["possibleTypes"] = getNestedInterfaceDefinitions(copy.deepcopy(curInterface["possibleTypes"]), curParamPath,childOperations, parentFields)
|
|
343
|
+
return curInterfaces
|
|
344
|
+
|
|
345
|
+
def parseOperation(curOperation,childOperations):
|
|
346
|
+
if "operationArgs" not in curOperation:
|
|
347
|
+
curOperation["operationArgs"] = {}
|
|
348
|
+
curOperation["fieldTypes"] = {}
|
|
349
|
+
curOfType = getOfType(curOperation["type"], { "non_null": False, "kind": [], "name": None }, None,childOperations,None)
|
|
350
|
+
curOperation["type"] = copy.deepcopy(curOfType)
|
|
351
|
+
if curOfType["name"] in catoApiIntrospection["objects"]:
|
|
352
|
+
curOperation["args"] = getNestedArgDefinitions(curOperation["args"], None,childOperations,None)
|
|
353
|
+
curOperation["type"]["definition"] = copy.deepcopy(catoApiIntrospection["objects"][curOperation["type"]["name"]])
|
|
354
|
+
if "fields" in curOperation["type"]["definition"] and curOperation["type"]["definition"]["fields"] != None:
|
|
355
|
+
# aliasLogic
|
|
356
|
+
# parentFields = []
|
|
357
|
+
# for field in curOperation["type"]["definition"]["fields"]:
|
|
358
|
+
# parentFields.append(field["name"])
|
|
359
|
+
curOperation["type"]["definition"]["fields"] = checkForChildOperation(copy.deepcopy(curOperation["type"]["definition"]["fields"]),childOperations)
|
|
360
|
+
curOperation["type"]["definition"]["fields"] = copy.deepcopy(getNestedFieldDefinitions(curOperation["type"]["definition"]["fields"], None,childOperations,[]))
|
|
361
|
+
if "inputFields" in curOperation["type"]["definition"] and curOperation["type"]["definition"]["inputFields"] != None:
|
|
362
|
+
parentFields = curOperation["type"]["definition"]["inputFields"].keys()
|
|
363
|
+
curOperation["type"]["definition"]["inputFields"] = copy.deepcopy(getNestedFieldDefinitions(curOperation["type"]["definition"]["inputFields"], None,childOperations,parentFields))
|
|
364
|
+
return curOperation
|
|
365
|
+
|
|
366
|
+
def checkForChildOperation(fieldsAry,childOperations):
|
|
367
|
+
newFieldList = {}
|
|
368
|
+
subOperation = False
|
|
369
|
+
for i, field in enumerate(fieldsAry):
|
|
370
|
+
if field["name"] in childOperations:
|
|
371
|
+
subOperation = field
|
|
372
|
+
newFieldList[field["name"]] = copy.deepcopy(field)
|
|
373
|
+
if subOperation != False:
|
|
374
|
+
newFieldList = {}
|
|
375
|
+
newFieldList[subOperation["name"]] = subOperation
|
|
376
|
+
return newFieldList
|
|
377
|
+
|
|
378
|
+
def getOperationArgs(curType,curOperation):
|
|
379
|
+
if curType.get('fields'):
|
|
380
|
+
for fieldName in curType["fields"]:
|
|
381
|
+
field = curType["fields"][fieldName]
|
|
382
|
+
## aliasLogic
|
|
383
|
+
if "type" in field and "definition" in field["type"]:
|
|
384
|
+
curOperation["fieldTypes"][field["type"]["definition"]["name"]] = True
|
|
385
|
+
curOperation = getOperationArgs(field["type"]["definition"],curOperation)
|
|
386
|
+
if "args" in field:
|
|
387
|
+
for argName in field["args"]:
|
|
388
|
+
arg = field["args"][argName]
|
|
389
|
+
curOperation["operationArgs"][arg["varName"]] = arg
|
|
390
|
+
if "type" in arg and "definition" in arg["type"]:
|
|
391
|
+
curOperation = getOperationArgs(arg["type"]["definition"],curOperation)
|
|
392
|
+
if curType.get('inputFields'):
|
|
393
|
+
for inputFieldName in curType["inputFields"]:
|
|
394
|
+
inputField = curType["inputFields"][inputFieldName]
|
|
395
|
+
if "type" in inputField and "definition" in inputField["type"]:
|
|
396
|
+
curOperation["fieldTypes"][inputField["type"]["definition"]["name"]] = True
|
|
397
|
+
curOperation = getOperationArgs(inputField["type"]["definition"],curOperation)
|
|
398
|
+
if "args" in inputField:
|
|
399
|
+
for argName in inputField["args"]:
|
|
400
|
+
arg = inputField["args"][argName]
|
|
401
|
+
curOperation["operationArgs"][arg["varName"]] = arg
|
|
402
|
+
if "type" in arg and "definition" in arg["type"]:
|
|
403
|
+
curOperation = getOperationArgs(arg["type"]["definition"],curOperation)
|
|
404
|
+
if curType.get('interfaces'):
|
|
405
|
+
for interface in curType["interfaces"]:
|
|
406
|
+
if "type" in interface and "definition" in interface["type"]:
|
|
407
|
+
curOperation["fieldTypes"][interface["type"]["definition"]["name"]] = True
|
|
408
|
+
curOperation = getOperationArgs(interface["type"]["definition"],curOperation)
|
|
409
|
+
if "args" in interface:
|
|
410
|
+
for argName in interface["args"]:
|
|
411
|
+
arg = interface["args"][argName]
|
|
412
|
+
curOperation["operationArgs"][arg["varName"]] = arg
|
|
413
|
+
if "type" in arg and "definition" in arg["type"]:
|
|
414
|
+
curOperation = getOperationArgs(arg["type"]["definition"],curOperation)
|
|
415
|
+
if curType.get('possibleTypes'):
|
|
416
|
+
for possibleTypeName in curType["possibleTypes"]:
|
|
417
|
+
possibleType = curType["possibleTypes"][possibleTypeName]
|
|
418
|
+
curOperation = getOperationArgs(possibleType,curOperation)
|
|
419
|
+
if possibleType.get('fields'):
|
|
420
|
+
for fieldName in possibleType["fields"]:
|
|
421
|
+
field = possibleType["fields"][fieldName]
|
|
422
|
+
## aliasLogic
|
|
423
|
+
# if "type" in curOperation and "definition" in curOperation["type"] and "fields" in curOperation["type"]["definition"] and field["name"] in curOperation["type"]["definition"]["fields"]:
|
|
424
|
+
# # if field["type"]["definition"]["fields"]==None or field["type"]["definition"]["inputFields"]:
|
|
425
|
+
# # if "SCALAR" not in field["type"]["kind"] or "ENUM" not in field["type"]["kind"]:
|
|
426
|
+
# field["alias"] = renderCamelCase(possibleTypeName+"."+field["name"])+": "+field["name"]
|
|
427
|
+
if "args" in possibleType:
|
|
428
|
+
for argName in possibleType["args"]:
|
|
429
|
+
arg = possibleType["args"][argName]
|
|
430
|
+
curOperation["operationArgs"][arg["varName"]] = arg
|
|
431
|
+
if "type" in arg and "definition" in arg["type"]:
|
|
432
|
+
curOperation = getOperationArgs(arg["type"]["definition"],curOperation)
|
|
433
|
+
return curOperation
|
|
434
|
+
|
|
435
|
+
def renderCamelCase(pathStr):
|
|
436
|
+
str = ""
|
|
437
|
+
pathAry = pathStr.split(".")
|
|
438
|
+
for i, path in enumerate(pathAry):
|
|
439
|
+
if i == 0:
|
|
440
|
+
str += path[0].lower() + path[1:]
|
|
441
|
+
else:
|
|
442
|
+
str += path[0].upper() + path[1:]
|
|
443
|
+
return str
|
|
444
|
+
|
|
445
|
+
## Functions to create sample nested variable objects for cli arguments ##
|
|
446
|
+
def generateExampleVariables(operation):
|
|
447
|
+
variablesObj = {}
|
|
448
|
+
for argName in operation["operationArgs"]:
|
|
449
|
+
arg = operation["operationArgs"][argName]
|
|
450
|
+
if "SCALAR" in arg["type"]["kind"] or "ENUM" in arg["type"]["kind"]:
|
|
451
|
+
variablesObj[arg["name"]] = renderInputFieldVal(arg)
|
|
452
|
+
else:
|
|
453
|
+
argTD = arg["type"]["definition"]
|
|
454
|
+
variablesObj[arg["varName"]] = {}
|
|
455
|
+
if "inputFields" in argTD and argTD["inputFields"] != None:
|
|
456
|
+
for inputFieldName in argTD["inputFields"]:
|
|
457
|
+
inputField = argTD["inputFields"][inputFieldName]
|
|
458
|
+
variablesObj[arg["varName"]][inputField["varName"]] = parseNestedArgFields(inputField)
|
|
459
|
+
if "fields" in argTD and argTD["fields"] != None:
|
|
460
|
+
for fieldName in argTD["fields"]:
|
|
461
|
+
field = argTD["fields"][fieldName]
|
|
462
|
+
variablesObj[arg["varName"]][field["varName"]] = parseNestedArgFields(field)
|
|
463
|
+
if "possibleTypes" in argTD and argTD["possibleTypes"] != None:
|
|
464
|
+
for possibleTypeName in argTD["possibleTypes"]:
|
|
465
|
+
possibleType = argTD["possibleTypes"][possibleTypeName]
|
|
466
|
+
variablesObj[arg["varName"]][possibleType["varName"]] = parseNestedArgFields(possibleTypeName)
|
|
467
|
+
if "accountID" in variablesObj:
|
|
468
|
+
del variablesObj["accountID"]
|
|
469
|
+
if "accountId" in variablesObj:
|
|
470
|
+
del variablesObj["accountId"]
|
|
471
|
+
return variablesObj
|
|
472
|
+
|
|
473
|
+
def parseNestedArgFields(fieldObj):
|
|
474
|
+
subVariableObj = {}
|
|
475
|
+
if "SCALAR" in fieldObj["type"]["kind"] or "ENUM" in fieldObj["type"]["kind"]:
|
|
476
|
+
subVariableObj[fieldObj["name"]] = renderInputFieldVal(fieldObj)
|
|
477
|
+
else:
|
|
478
|
+
fieldTD = fieldObj["type"]["definition"]
|
|
479
|
+
if "inputFields" in fieldTD and fieldTD["inputFields"] != None:
|
|
480
|
+
for inputFieldName in fieldTD["inputFields"]:
|
|
481
|
+
inputField = fieldTD["inputFields"][inputFieldName]
|
|
482
|
+
subVariableObj[inputField["name"]] = parseNestedArgFields(inputField)
|
|
483
|
+
if "fields" in fieldTD and fieldTD["fields"] != None:
|
|
484
|
+
for fieldName in fieldTD["fields"]:
|
|
485
|
+
field = fieldTD["fields"][fieldName]
|
|
486
|
+
subVariableObj[field["name"]] = parseNestedArgFields(field)
|
|
487
|
+
if "possibleTypes" in fieldTD and fieldTD["possibleTypes"] != None:
|
|
488
|
+
for possibleTypeName in fieldTD["possibleTypes"]:
|
|
489
|
+
possibleType = fieldTD["possibleTypes"][possibleTypeName]
|
|
490
|
+
subVariableObj[possibleType["name"]] = parseNestedArgFields(possibleTypeName)
|
|
491
|
+
return subVariableObj
|
|
492
|
+
|
|
493
|
+
def renderInputFieldVal(arg):
|
|
494
|
+
value = "string"
|
|
495
|
+
if "SCALAR" in arg["type"]["kind"]:
|
|
496
|
+
if "LIST" in arg["type"]["kind"]:
|
|
497
|
+
value = [arg["type"]["name"]]
|
|
498
|
+
else:
|
|
499
|
+
value = arg["type"]["name"]
|
|
500
|
+
elif "ENUM" in arg["type"]["kind"]:
|
|
501
|
+
value = "enum("+arg["type"]["name"]+")"
|
|
502
|
+
# arg["type"]["definition"]["enumValues"][0]["name"]
|
|
503
|
+
return value
|
|
504
|
+
|
|
505
|
+
def writeCliDriver(catoApiSchema):
|
|
506
|
+
parsersIndex = {}
|
|
507
|
+
for operationType in catoApiSchema:
|
|
508
|
+
for operation in catoApiSchema[operationType]:
|
|
509
|
+
operationNameAry = operation.split(".")
|
|
510
|
+
parsersIndex[operationNameAry[0]+"_"+operationNameAry[1]] = True
|
|
511
|
+
parsers = list(parsersIndex.keys())
|
|
512
|
+
|
|
513
|
+
cliDriverStr = """
|
|
514
|
+
import os
|
|
515
|
+
import argparse
|
|
516
|
+
import json
|
|
517
|
+
import catocli
|
|
518
|
+
from graphql_client import Configuration
|
|
519
|
+
from graphql_client.api_client import ApiException
|
|
520
|
+
from ..parsers.parserApiClient import get_help
|
|
521
|
+
from .profile_manager import get_profile_manager
|
|
522
|
+
from .version_checker import check_for_updates, force_check_updates
|
|
523
|
+
import traceback
|
|
524
|
+
import sys
|
|
525
|
+
sys.path.insert(0, 'vendor')
|
|
526
|
+
import urllib3
|
|
527
|
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
528
|
+
# Initialize profile manager
|
|
529
|
+
profile_manager = get_profile_manager()
|
|
530
|
+
CATO_DEBUG = bool(os.getenv("CATO_DEBUG", False))
|
|
531
|
+
from ..parsers.raw import raw_parse
|
|
532
|
+
from ..parsers.custom import custom_parse
|
|
533
|
+
from ..parsers.query_siteLocation import query_siteLocation_parse
|
|
534
|
+
"""
|
|
535
|
+
for parserName in parsers:
|
|
536
|
+
cliDriverStr += "from ..parsers."+parserName+" import "+parserName+"_parse\n"
|
|
537
|
+
|
|
538
|
+
cliDriverStr += """
|
|
539
|
+
def show_version_info(args, configuration=None):
|
|
540
|
+
print(f"catocli version {catocli.__version__}")
|
|
541
|
+
|
|
542
|
+
if not args.current_only:
|
|
543
|
+
if args.check_updates:
|
|
544
|
+
# Force check for updates
|
|
545
|
+
is_newer, latest_version, source = force_check_updates()
|
|
546
|
+
else:
|
|
547
|
+
# Regular check (uses cache)
|
|
548
|
+
is_newer, latest_version, source = check_for_updates(show_if_available=False)
|
|
549
|
+
|
|
550
|
+
if latest_version:
|
|
551
|
+
if is_newer:
|
|
552
|
+
print(f"Latest version: {latest_version} (from {source}) - UPDATE AVAILABLE!")
|
|
553
|
+
print()
|
|
554
|
+
print("To upgrade, run:")
|
|
555
|
+
print("pip install --upgrade catocli")
|
|
556
|
+
else:
|
|
557
|
+
print(f"Latest version: {latest_version} (from {source}) - You are up to date!")
|
|
558
|
+
else:
|
|
559
|
+
print("Unable to check for updates (check your internet connection)")
|
|
560
|
+
return [{"success": True, "current_version": catocli.__version__, "latest_version": latest_version if not args.current_only else None}]
|
|
561
|
+
|
|
562
|
+
def get_configuration(skip_api_key=False):
|
|
563
|
+
configuration = Configuration()
|
|
564
|
+
configuration.verify_ssl = False
|
|
565
|
+
configuration.debug = CATO_DEBUG
|
|
566
|
+
configuration.version = "{}".format(catocli.__version__)
|
|
567
|
+
|
|
568
|
+
# Try to migrate from environment variables first
|
|
569
|
+
profile_manager.migrate_from_environment()
|
|
570
|
+
|
|
571
|
+
# Get credentials from profile
|
|
572
|
+
credentials = profile_manager.get_credentials()
|
|
573
|
+
if not credentials:
|
|
574
|
+
print("No Cato CLI profile configured.")
|
|
575
|
+
print("Run 'catocli configure set' to set up your credentials.")
|
|
576
|
+
exit(1)
|
|
577
|
+
|
|
578
|
+
if not credentials.get('cato_token') or not credentials.get('account_id'):
|
|
579
|
+
profile_name = profile_manager.get_current_profile()
|
|
580
|
+
print(f"Profile '{profile_name}' is missing required credentials.")
|
|
581
|
+
print(f"Run 'catocli configure set --profile {profile_name}' to update your credentials.")
|
|
582
|
+
exit(1)
|
|
583
|
+
|
|
584
|
+
# Only set API key if not using custom headers file
|
|
585
|
+
if not skip_api_key:
|
|
586
|
+
configuration.api_key["x-api-key"] = credentials['cato_token']
|
|
587
|
+
configuration.host = credentials['endpoint']
|
|
588
|
+
configuration.accountID = credentials['account_id']
|
|
589
|
+
|
|
590
|
+
return configuration
|
|
591
|
+
|
|
592
|
+
defaultReadmeStr = \"""
|
|
593
|
+
The Cato CLI is a command-line interface tool designed to simplify the management and automation of Cato Networks’ configurations and operations.
|
|
594
|
+
It enables users to interact with Cato’s API for tasks such as managing Cato Management Application (CMA) site and account configurations, security policies, retrieving events, etc.\n\n
|
|
595
|
+
For assistance in generating syntax for the cli to perform various operations, please refer to the Cato API Explorer application.\n\n
|
|
596
|
+
https://github.com/catonetworks/cato-api-explorer
|
|
597
|
+
\"""
|
|
598
|
+
|
|
599
|
+
parser = argparse.ArgumentParser(prog='catocli', usage='%(prog)s <operationType> <operationName> [options]', description=defaultReadmeStr)
|
|
600
|
+
parser.add_argument('--version', action='version', version=catocli.__version__)
|
|
601
|
+
parser.add_argument('-H', '--header', action='append', dest='headers', help='Add custom headers in "Key: Value" format. Can be used multiple times.')
|
|
602
|
+
parser.add_argument('--headers-file', dest='headers_file', help='Load headers from a file. Each line should contain a header in "Key: Value" format.')
|
|
603
|
+
subparsers = parser.add_subparsers()
|
|
604
|
+
|
|
605
|
+
# Version command - enhanced with update checking
|
|
606
|
+
version_parser = subparsers.add_parser('version', help='Show version information and check for updates')
|
|
607
|
+
version_parser.add_argument('--check-updates', action='store_true', help='Force check for updates (ignores cache)')
|
|
608
|
+
version_parser.add_argument('--current-only', action='store_true', help='Show only current version')
|
|
609
|
+
version_parser.set_defaults(func=show_version_info)
|
|
610
|
+
|
|
611
|
+
custom_parsers = custom_parse(subparsers)
|
|
612
|
+
raw_parsers = subparsers.add_parser('raw', help='Raw GraphQL', usage=get_help("raw"))
|
|
613
|
+
raw_parser = raw_parse(raw_parsers)
|
|
614
|
+
query_parser = subparsers.add_parser('query', help='Query', usage='catocli query <operationName> [options]')
|
|
615
|
+
query_subparsers = query_parser.add_subparsers(description='valid subcommands', help='additional help')
|
|
616
|
+
query_siteLocation_parser = query_siteLocation_parse(query_subparsers)
|
|
617
|
+
mutation_parser = subparsers.add_parser('mutation', help='Mutation', usage='catocli mutation <operationName> [options]')
|
|
618
|
+
mutation_subparsers = mutation_parser.add_subparsers(description='valid subcommands', help='additional help')
|
|
619
|
+
|
|
620
|
+
"""
|
|
621
|
+
for parserName in parsers:
|
|
622
|
+
cliDriverStr += parserName+"_parser = "+parserName+"_parse("+parserName.split("_").pop(0)+"_subparsers)\n"
|
|
623
|
+
|
|
624
|
+
cliDriverStr += """
|
|
625
|
+
|
|
626
|
+
def parse_headers(header_strings):
|
|
627
|
+
headers = {}
|
|
628
|
+
if header_strings:
|
|
629
|
+
for header_string in header_strings:
|
|
630
|
+
if ':' not in header_string:
|
|
631
|
+
print(f"ERROR: Invalid header format '{header_string}'. Use 'Key: Value' format.")
|
|
632
|
+
exit(1)
|
|
633
|
+
key, value = header_string.split(':', 1)
|
|
634
|
+
headers[key.strip()] = value.strip()
|
|
635
|
+
return headers
|
|
636
|
+
|
|
637
|
+
def parse_headers_from_file(file_path):
|
|
638
|
+
headers = {}
|
|
639
|
+
try:
|
|
640
|
+
with open(file_path, 'r') as f:
|
|
641
|
+
for line_num, line in enumerate(f, 1):
|
|
642
|
+
line = line.strip()
|
|
643
|
+
if not line or line.startswith('#'):
|
|
644
|
+
# Skip empty lines and comments
|
|
645
|
+
continue
|
|
646
|
+
if ':' not in line:
|
|
647
|
+
print(f"ERROR: Invalid header format in {file_path} at line {line_num}: '{line}'. Use 'Key: Value' format.")
|
|
648
|
+
exit(1)
|
|
649
|
+
key, value = line.split(':', 1)
|
|
650
|
+
headers[key.strip()] = value.strip()
|
|
651
|
+
except FileNotFoundError:
|
|
652
|
+
print(f"ERROR: Headers file '{file_path}' not found.")
|
|
653
|
+
exit(1)
|
|
654
|
+
except IOError as e:
|
|
655
|
+
print(f"ERROR: Could not read headers file '{file_path}': {e}")
|
|
656
|
+
exit(1)
|
|
657
|
+
return headers
|
|
658
|
+
|
|
659
|
+
def main(args=None):
|
|
660
|
+
# Check if no arguments provided or help is requested
|
|
661
|
+
if args is None:
|
|
662
|
+
args = sys.argv[1:]
|
|
663
|
+
|
|
664
|
+
# Show version check when displaying help or when no command specified
|
|
665
|
+
if not args or '-h' in args or '--help' in args:
|
|
666
|
+
# Check for updates in background (non-blocking)
|
|
667
|
+
try:
|
|
668
|
+
check_for_updates(show_if_available=True)
|
|
669
|
+
except Exception:
|
|
670
|
+
# Don't let version check interfere with CLI operation
|
|
671
|
+
pass
|
|
672
|
+
|
|
673
|
+
args = parser.parse_args(args=args)
|
|
674
|
+
try:
|
|
675
|
+
# Skip authentication for configure commands
|
|
676
|
+
if hasattr(args, 'func') and hasattr(args.func, '__module__') and 'configure' in str(args.func.__module__):
|
|
677
|
+
response = args.func(args, None)
|
|
678
|
+
else:
|
|
679
|
+
# Check if using headers file to determine if we should skip API key
|
|
680
|
+
using_headers_file = hasattr(args, 'headers_file') and args.headers_file
|
|
681
|
+
|
|
682
|
+
# Get configuration from profiles
|
|
683
|
+
configuration = get_configuration(skip_api_key=using_headers_file)
|
|
684
|
+
|
|
685
|
+
# Parse custom headers if provided
|
|
686
|
+
custom_headers = {}
|
|
687
|
+
if hasattr(args, 'headers') and args.headers:
|
|
688
|
+
custom_headers.update(parse_headers(args.headers))
|
|
689
|
+
if hasattr(args, 'headers_file') and args.headers_file:
|
|
690
|
+
custom_headers.update(parse_headers_from_file(args.headers_file))
|
|
691
|
+
if custom_headers:
|
|
692
|
+
configuration.custom_headers.update(custom_headers)
|
|
693
|
+
# Handle account ID override
|
|
694
|
+
if args.func.__name__ != "createRawRequest":
|
|
695
|
+
if hasattr(args, 'accountID') and args.accountID is not None:
|
|
696
|
+
# Command line override takes precedence
|
|
697
|
+
configuration.accountID = args.accountID
|
|
698
|
+
# Otherwise use the account ID from the profile (already set in get_configuration)
|
|
699
|
+
response = args.func(args, configuration)
|
|
700
|
+
|
|
701
|
+
if type(response) == ApiException:
|
|
702
|
+
print("ERROR! Status code: {}".format(response.status))
|
|
703
|
+
print(response)
|
|
704
|
+
else:
|
|
705
|
+
if response!=None:
|
|
706
|
+
print(json.dumps(response[0], sort_keys=True, indent=4))
|
|
707
|
+
except Exception as e:
|
|
708
|
+
if isinstance(e, AttributeError):
|
|
709
|
+
print('Missing arguments. Usage: catocli <operation> -h')
|
|
710
|
+
if args.v==True:
|
|
711
|
+
print('ERROR: ',e)
|
|
712
|
+
traceback.print_exc()
|
|
713
|
+
else:
|
|
714
|
+
print('ERROR: ',e)
|
|
715
|
+
traceback.print_exc()
|
|
716
|
+
exit(1)
|
|
717
|
+
"""
|
|
718
|
+
writeFile("../catocli/Utils/clidriver.py",cliDriverStr)
|
|
719
|
+
|
|
720
|
+
def writeOperationParsers(catoApiSchema):
|
|
721
|
+
parserMapping = {"query":{},"mutation":{}}
|
|
722
|
+
## Write the raw query parser ##
|
|
723
|
+
cliDriverStr =f"""
|
|
724
|
+
from ..parserApiClient import createRawRequest, get_help
|
|
725
|
+
|
|
726
|
+
def raw_parse(raw_parser):
|
|
727
|
+
raw_parser.add_argument('json', nargs='?', default='{{}}', help='Query, Variables and opertaionName in JSON format (defaults to empty object if not provided).')
|
|
728
|
+
raw_parser.add_argument('-t', const=True, default=False, nargs='?', help='Print GraphQL query without sending API call')
|
|
729
|
+
raw_parser.add_argument('-v', const=True, default=False, nargs='?', help='Verbose output')
|
|
730
|
+
raw_parser.add_argument('-p', const=True, default=False, nargs='?', help='Pretty print')
|
|
731
|
+
raw_parser.add_argument('-H', '--header', action='append', dest='headers', help='Add custom headers in "Key: Value" format. Can be used multiple times.')
|
|
732
|
+
raw_parser.add_argument('--headers-file', dest='headers_file', help='Load headers from a file. Each line should contain a header in "Key: Value" format.')
|
|
733
|
+
raw_parser.add_argument('--endpoint', dest='endpoint', help='Override the API endpoint URL (e.g., https://api.catonetworks.com/api/v1/graphql2)')
|
|
734
|
+
raw_parser.set_defaults(func=createRawRequest,operation_name='raw')
|
|
735
|
+
"""
|
|
736
|
+
parserPath = "../catocli/parsers/raw"
|
|
737
|
+
if not os.path.exists(parserPath):
|
|
738
|
+
os.makedirs(parserPath)
|
|
739
|
+
writeFile(parserPath+"/__init__.py",cliDriverStr)
|
|
740
|
+
|
|
741
|
+
## Write the siteLocation query parser ##
|
|
742
|
+
cliDriverStr =f"""
|
|
743
|
+
from ..parserApiClient import querySiteLocation, get_help
|
|
744
|
+
|
|
745
|
+
def query_siteLocation_parse(query_subparsers):
|
|
746
|
+
query_siteLocation_parser = query_subparsers.add_parser('siteLocation',
|
|
747
|
+
help='siteLocation local cli query',
|
|
748
|
+
usage=get_help("query_siteLocation"))
|
|
749
|
+
query_siteLocation_parser.add_argument('json', nargs='?', default='{{}}', help='Variables in JSON format (defaults to empty object if not provided).')
|
|
750
|
+
query_siteLocation_parser.add_argument('-accountID', help='Override the CATO_ACCOUNT_ID environment variable with this value.')
|
|
751
|
+
query_siteLocation_parser.add_argument('-t', const=True, default=False, nargs='?', help='Print GraphQL query without sending API call')
|
|
752
|
+
query_siteLocation_parser.add_argument('-v', const=True, default=False, nargs='?', help='Verbose output')
|
|
753
|
+
query_siteLocation_parser.add_argument('-p', const=True, default=False, nargs='?', help='Pretty print')
|
|
754
|
+
query_siteLocation_parser.set_defaults(func=querySiteLocation,operation_name='query.siteLocation')
|
|
755
|
+
"""
|
|
756
|
+
parserPath = "../catocli/parsers/query_siteLocation"
|
|
757
|
+
if not os.path.exists(parserPath):
|
|
758
|
+
os.makedirs(parserPath)
|
|
759
|
+
writeFile(parserPath+"/__init__.py",cliDriverStr)
|
|
760
|
+
|
|
761
|
+
for operationType in parserMapping:
|
|
762
|
+
operationAry = catoApiSchema[operationType]
|
|
763
|
+
for operationName in operationAry:
|
|
764
|
+
parserMapping = getParserMapping(parserMapping,operationName,operationName,operationAry[operationName])
|
|
765
|
+
for operationType in parserMapping:
|
|
766
|
+
for operationName in parserMapping[operationType]:
|
|
767
|
+
parserName = operationType+"_"+operationName
|
|
768
|
+
parser = parserMapping[operationType][operationName]
|
|
769
|
+
cliDriverStr = f"""
|
|
770
|
+
from ..parserApiClient import createRequest, get_help
|
|
771
|
+
|
|
772
|
+
def {parserName}_parse({operationType}_subparsers):
|
|
773
|
+
{parserName}_parser = {operationType}_subparsers.add_parser('{operationName}',
|
|
774
|
+
help='{operationName}() {operationType} operation',
|
|
775
|
+
usage=get_help("{operationType}_{operationName}"))
|
|
776
|
+
"""
|
|
777
|
+
if "path" in parser:
|
|
778
|
+
cliDriverStr += f"""
|
|
779
|
+
{parserName}_parser.add_argument('json', nargs='?', default='{{}}', help='Variables in JSON format (defaults to empty object if not provided).')
|
|
780
|
+
{parserName}_parser.add_argument('-accountID', help='Override the CATO_ACCOUNT_ID environment variable with this value.')
|
|
781
|
+
{parserName}_parser.add_argument('-t', const=True, default=False, nargs='?', help='Print GraphQL query without sending API call')
|
|
782
|
+
{parserName}_parser.add_argument('-v', const=True, default=False, nargs='?', help='Verbose output')
|
|
783
|
+
{parserName}_parser.add_argument('-p', const=True, default=False, nargs='?', help='Pretty print')
|
|
784
|
+
{parserName}_parser.add_argument('-H', '--header', action='append', dest='headers', help='Add custom headers in "Key: Value" format. Can be used multiple times.')
|
|
785
|
+
{parserName}_parser.add_argument('--headers-file', dest='headers_file', help='Load headers from a file. Each line should contain a header in "Key: Value" format.')
|
|
786
|
+
{parserName}_parser.set_defaults(func=createRequest,operation_name='{parserName.replace("_",".")}')
|
|
787
|
+
"""
|
|
788
|
+
else:
|
|
789
|
+
cliDriverStr += renderSubParser(parser,operationType+"_"+operationName)
|
|
790
|
+
parserPath = "../catocli/parsers/"+parserName
|
|
791
|
+
if not os.path.exists(parserPath):
|
|
792
|
+
os.makedirs(parserPath)
|
|
793
|
+
writeFile(parserPath+"/__init__.py",cliDriverStr)
|
|
794
|
+
|
|
795
|
+
def renderSubParser(subParser,parentParserPath):
|
|
796
|
+
cliDriverStr = f"""
|
|
797
|
+
{parentParserPath}_subparsers = {parentParserPath}_parser.add_subparsers()
|
|
798
|
+
"""
|
|
799
|
+
for subOperationName in subParser:
|
|
800
|
+
subOperation = subParser[subOperationName]
|
|
801
|
+
subParserPath = parentParserPath.replace(".","_")+"_"+subOperationName
|
|
802
|
+
cliDriverStr += f"""
|
|
803
|
+
{subParserPath}_parser = {parentParserPath}_subparsers.add_parser('{subOperationName}',
|
|
804
|
+
help='{subOperationName}() {parentParserPath.split('_').pop()} operation',
|
|
805
|
+
usage=get_help("{subParserPath}"))
|
|
806
|
+
"""
|
|
807
|
+
if "path" in subOperation:
|
|
808
|
+
command = parentParserPath.replace("_"," ")+" "+subOperationName
|
|
809
|
+
cliDriverStr += f"""
|
|
810
|
+
{subParserPath}_parser.add_argument('json', nargs='?', default='{{}}', help='Variables in JSON format (defaults to empty object if not provided).')
|
|
811
|
+
{subParserPath}_parser.add_argument('-accountID', help='Override the CATO_ACCOUNT_ID environment variable with this value.')
|
|
812
|
+
{subParserPath}_parser.add_argument('-t', const=True, default=False, nargs='?', help='Print GraphQL query without sending API call')
|
|
813
|
+
{subParserPath}_parser.add_argument('-v', const=True, default=False, nargs='?', help='Verbose output')
|
|
814
|
+
{subParserPath}_parser.add_argument('-p', const=True, default=False, nargs='?', help='Pretty print')
|
|
815
|
+
{subParserPath}_parser.add_argument('-H', '--header', action='append', dest='headers', help='Add custom headers in "Key: Value" format. Can be used multiple times.')
|
|
816
|
+
{subParserPath}_parser.add_argument('--headers-file', dest='headers_file', help='Load headers from a file. Each line should contain a header in "Key: Value" format.')
|
|
817
|
+
{subParserPath}_parser.set_defaults(func=createRequest,operation_name='{subOperation["path"]}')
|
|
818
|
+
"""
|
|
819
|
+
else:
|
|
820
|
+
cliDriverStr += renderSubParser(subOperation,subParserPath)
|
|
821
|
+
return cliDriverStr
|
|
822
|
+
|
|
823
|
+
def writeReadmes(catoApiSchema):
|
|
824
|
+
parserMapping = {"query":{},"mutation":{}}
|
|
825
|
+
|
|
826
|
+
## Write the raw query readme ##
|
|
827
|
+
readmeStr = """
|
|
828
|
+
## CATO-CLI - raw.graphql
|
|
829
|
+
[Click here](https://api.catonetworks.com/documentation/) for documentation on this operation.
|
|
830
|
+
|
|
831
|
+
### Usage for raw.graphql
|
|
832
|
+
|
|
833
|
+
`catocli raw -h`
|
|
834
|
+
|
|
835
|
+
`catocli raw <json>`
|
|
836
|
+
|
|
837
|
+
`catocli raw "$(cat < rawGraphqQL.json)"`
|
|
838
|
+
|
|
839
|
+
`catocli raw '{ "query": "query operationNameHere($yourArgument:String!) { field1 field2 }", "variables": { "yourArgument": "string", "accountID": "10949" }, "operationName": "operationNameHere" } '`
|
|
840
|
+
|
|
841
|
+
`catocli raw '{ "query": "mutation operationNameHere($yourArgument:String!) { field1 field2 }", "variables": { "yourArgument": "string", "accountID": "10949" }, "operationName": "operationNameHere" } '`
|
|
842
|
+
|
|
843
|
+
#### Override API endpoint
|
|
844
|
+
|
|
845
|
+
`catocli raw --endpoint https://custom-api.example.com/graphql '<json>'`
|
|
846
|
+
"""
|
|
847
|
+
parserPath = "../catocli/parsers/raw"
|
|
848
|
+
if not os.path.exists(parserPath):
|
|
849
|
+
os.makedirs(parserPath)
|
|
850
|
+
writeFile(parserPath+"/README.md",readmeStr)
|
|
851
|
+
|
|
852
|
+
## Write the query.siteLocation readme ##
|
|
853
|
+
readmeStr = """
|
|
854
|
+
|
|
855
|
+
## CATO-CLI - query.siteLocation:
|
|
856
|
+
|
|
857
|
+
### Usage for query.siteLocation:
|
|
858
|
+
|
|
859
|
+
`catocli query siteLocation -h`
|
|
860
|
+
|
|
861
|
+
`catocli query siteLocation <json>`
|
|
862
|
+
|
|
863
|
+
`catocli query siteLocation "$(cat < siteLocation.json)"`
|
|
864
|
+
|
|
865
|
+
`catocli query siteLocation '{"filters":[{"search": "Your city here","field":"city","operation":"exact"}]}'`
|
|
866
|
+
|
|
867
|
+
`catocli query siteLocation '{"filters":[{"search": "Your Country here","field":"countryName","operation":"startsWith"}]}'`
|
|
868
|
+
|
|
869
|
+
`catocli query siteLocation '{"filters":[{"search": "Your stateName here","field":"stateName","operation":"endsWith"}]}'`
|
|
870
|
+
|
|
871
|
+
`catocli query siteLocation '{"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"}]}'`
|
|
872
|
+
|
|
873
|
+
#### Operation Arguments for query.siteLocation ####
|
|
874
|
+
`accountID` [ID] - (required) Unique Identifier of Account.
|
|
875
|
+
`filters[]` [Array] - (optional) Array of objects consisting of `search`, `field` and `operation` attributes.
|
|
876
|
+
`filters[].search` [String] - (required) String to match countryName, stateName, or city specificed in `filters[].field`.
|
|
877
|
+
`filters[].field` [String] - (required) Specify field to match query against, defaults to look for any. Possible values: `countryName`, `stateName`, or `city`.
|
|
878
|
+
`filters[].operation` [string] - (required) If a field is specified, operation to match the field value. Possible values: `startsWith`,`endsWith`,`exact`, `contains`.
|
|
879
|
+
"""
|
|
880
|
+
parserPath = "../catocli/parsers/query_siteLocation"
|
|
881
|
+
if not os.path.exists(parserPath):
|
|
882
|
+
os.makedirs(parserPath)
|
|
883
|
+
writeFile(parserPath+"/README.md",readmeStr)
|
|
884
|
+
|
|
885
|
+
for operationType in parserMapping:
|
|
886
|
+
operationAry = catoApiSchema[operationType]
|
|
887
|
+
for operationName in operationAry:
|
|
888
|
+
parserMapping = getParserMapping(parserMapping,operationName,operationName,operationAry[operationName])
|
|
889
|
+
for operationType in parserMapping:
|
|
890
|
+
for operationName in parserMapping[operationType]:
|
|
891
|
+
parserName = operationType+"_"+operationName
|
|
892
|
+
parser = parserMapping[operationType][operationName]
|
|
893
|
+
operationPath = operationType+"."+operationName
|
|
894
|
+
operationCmd = operationType+" "+operationName
|
|
895
|
+
readmeStr = f"""
|
|
896
|
+
## CATO-CLI - {operationPath}:
|
|
897
|
+
[Click here](https://api.catonetworks.com/documentation/#{operationType}-{operationName}) for documentation on this operation.
|
|
898
|
+
|
|
899
|
+
### Usage for {operationPath}:
|
|
900
|
+
|
|
901
|
+
`catocli {operationCmd} -h`
|
|
902
|
+
"""
|
|
903
|
+
if "path" in parser:
|
|
904
|
+
readmeStr += f"""
|
|
905
|
+
`catocli {operationCmd} <json>`
|
|
906
|
+
|
|
907
|
+
`catocli {operationCmd} "$(cat < {operationName}.json)"`
|
|
908
|
+
|
|
909
|
+
`catocli {operationCmd} '{json.dumps(parser["example"])}'`
|
|
910
|
+
|
|
911
|
+
#### Operation Arguments for {operationPath} ####
|
|
912
|
+
"""
|
|
913
|
+
for argName in parser["args"]:
|
|
914
|
+
arg = parser["args"][argName]
|
|
915
|
+
readmeStr += '`'+argName+'` ['+arg["type"]+'] - '
|
|
916
|
+
readmeStr += '('+arg["required"]+') '+arg["description"]+' '
|
|
917
|
+
readmeStr += 'Default Value: '+str(arg["values"]) if len(arg["values"])>0 else ""
|
|
918
|
+
readmeStr += "\n"
|
|
919
|
+
parserPath = "../catocli/parsers/"+parserName
|
|
920
|
+
if not os.path.exists(parserPath):
|
|
921
|
+
os.makedirs(parserPath)
|
|
922
|
+
writeFile(parserPath+"/README.md",readmeStr)
|
|
923
|
+
else:
|
|
924
|
+
parserPath = "../catocli/parsers/"+parserName
|
|
925
|
+
if not os.path.exists(parserPath):
|
|
926
|
+
os.makedirs(parserPath)
|
|
927
|
+
writeFile(parserPath+"/README.md",readmeStr)
|
|
928
|
+
renderSubReadme(parser,operationType,operationType+"."+operationName)
|
|
929
|
+
|
|
930
|
+
def renderSubReadme(subParser,operationType,parentOperationPath):
|
|
931
|
+
for subOperationName in subParser:
|
|
932
|
+
subOperation = subParser[subOperationName]
|
|
933
|
+
subOperationPath = parentOperationPath+"."+subOperationName
|
|
934
|
+
subOperationCmd = parentOperationPath.replace("."," ")+" "+subOperationName
|
|
935
|
+
parserPath = "../catocli/parsers/"+subOperationPath.replace(".","_")
|
|
936
|
+
readmeStr = f"""
|
|
937
|
+
## CATO-CLI - {parentOperationPath}.{subOperationName}:
|
|
938
|
+
[Click here](https://api.catonetworks.com/documentation/#{operationType}-{subOperationName}) for documentation on this operation.
|
|
939
|
+
|
|
940
|
+
### Usage for {subOperationPath}:
|
|
941
|
+
|
|
942
|
+
`catocli {subOperationCmd} -h`
|
|
943
|
+
"""
|
|
944
|
+
if "path" in subOperation:
|
|
945
|
+
readmeStr += f"""
|
|
946
|
+
`catocli {subOperationCmd} <json>`
|
|
947
|
+
|
|
948
|
+
`catocli {subOperationCmd} "$(cat < {subOperationName}.json)"`
|
|
949
|
+
|
|
950
|
+
`catocli {subOperationCmd} '{json.dumps(subOperation["example"])}'`
|
|
951
|
+
|
|
952
|
+
#### Operation Arguments for {subOperationPath} ####
|
|
953
|
+
"""
|
|
954
|
+
for argName in subOperation["args"]:
|
|
955
|
+
arg = subOperation["args"][argName]
|
|
956
|
+
readmeStr += '`'+argName+'` ['+arg["type"]+'] - '
|
|
957
|
+
readmeStr += '('+arg["required"]+') '+arg["description"]+' '
|
|
958
|
+
readmeStr += 'Default Value: '+str(arg["values"]) if len(arg["values"])>0 else ""
|
|
959
|
+
readmeStr += "\n"
|
|
960
|
+
if not os.path.exists(parserPath):
|
|
961
|
+
os.makedirs(parserPath)
|
|
962
|
+
writeFile(parserPath+"/README.md",readmeStr)
|
|
963
|
+
else:
|
|
964
|
+
if not os.path.exists(parserPath):
|
|
965
|
+
os.makedirs(parserPath)
|
|
966
|
+
writeFile(parserPath+"/README.md",readmeStr)
|
|
967
|
+
renderSubReadme(subOperation,operationType,subOperationPath)
|
|
968
|
+
|
|
969
|
+
def getParserMapping(curParser,curPath,operationFullPath,operation):
|
|
970
|
+
parserObj = {
|
|
971
|
+
"path":operationFullPath,
|
|
972
|
+
"args":{},
|
|
973
|
+
# "example":"N/A",
|
|
974
|
+
"example":operation["variablesPayload"]
|
|
975
|
+
}
|
|
976
|
+
for argName in operation["operationArgs"]:
|
|
977
|
+
arg = operation["operationArgs"][argName]
|
|
978
|
+
values = []
|
|
979
|
+
if "definition" in arg["type"] and "enumValues" in arg["type"]["definition"] and arg["type"]["definition"]["enumValues"]!=None:
|
|
980
|
+
for enumValue in arg["type"]["definition"]["enumValues"]:
|
|
981
|
+
values.append(enumValue["name"])
|
|
982
|
+
parserObj["args"][arg["varName"]] = {
|
|
983
|
+
"name":arg["name"],
|
|
984
|
+
"description":"N/A" if arg["description"]==None else arg["description"],
|
|
985
|
+
"type":arg["type"]["name"]+("[]" if "LIST" in arg["type"]["kind"] else ""),
|
|
986
|
+
"required": "required" if arg["required"]==True else "optional",
|
|
987
|
+
"values":values
|
|
988
|
+
}
|
|
989
|
+
pAry = curPath.split(".")
|
|
990
|
+
pathCount = len(curPath.split("."))
|
|
991
|
+
if pAry[0] not in curParser:
|
|
992
|
+
curParser[pAry[0]] = {}
|
|
993
|
+
if pathCount == 2:
|
|
994
|
+
curParser[pAry[0]][pAry[1]] = parserObj
|
|
995
|
+
else:
|
|
996
|
+
if pAry[1] not in curParser[pAry[0]]:
|
|
997
|
+
curParser[pAry[0]][pAry[1]] = {}
|
|
998
|
+
if pathCount == 3:
|
|
999
|
+
curParser[pAry[0]][pAry[1]][pAry[2]] = parserObj
|
|
1000
|
+
else:
|
|
1001
|
+
if pAry[2] not in curParser[pAry[0]][pAry[1]]:
|
|
1002
|
+
curParser[pAry[0]][pAry[1]][pAry[2]] = {}
|
|
1003
|
+
if pathCount == 4:
|
|
1004
|
+
curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]] = parserObj
|
|
1005
|
+
else:
|
|
1006
|
+
if pAry[3] not in curParser[pAry[0]][pAry[1]][pAry[2]]:
|
|
1007
|
+
curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]] = {}
|
|
1008
|
+
if pathCount == 5:
|
|
1009
|
+
curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]][pAry[4]] = parserObj
|
|
1010
|
+
else:
|
|
1011
|
+
if pAry[4] not in curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]]:
|
|
1012
|
+
curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]][pAry[4]] = {}
|
|
1013
|
+
if pathCount == 6:
|
|
1014
|
+
curParser[pAry[0]][pAry[1]][pAry[2]][pAry[3]][pAry[4]][pAry[5]] = parserObj
|
|
1015
|
+
return curParser
|
|
1016
|
+
|
|
1017
|
+
def send(api_key,query,variables={},operationName=None):
|
|
1018
|
+
headers = { 'x-api-key': api_key,'Content-Type':'application/json'}
|
|
1019
|
+
no_verify = ssl._create_unverified_context()
|
|
1020
|
+
request = urllib.request.Request(url='https://api.catonetworks.com/api/v1/graphql2',
|
|
1021
|
+
data=json.dumps(query).encode("ascii"),headers=headers)
|
|
1022
|
+
response = urllib.request.urlopen(request, context=no_verify, timeout=60)
|
|
1023
|
+
result_data = response.read()
|
|
1024
|
+
result = json.loads(result_data)
|
|
1025
|
+
if "errors" in result:
|
|
1026
|
+
logging.warning(f"API error: {result_data}")
|
|
1027
|
+
return False,result
|
|
1028
|
+
return True,result
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
################### adding functions local to generate dynamic payloads ####################
|
|
1032
|
+
def generateGraphqlPayload(variablesObj,operation,operationName):
|
|
1033
|
+
indent = " "
|
|
1034
|
+
queryStr = ""
|
|
1035
|
+
variableStr = ""
|
|
1036
|
+
for varName in variablesObj:
|
|
1037
|
+
if (varName in operation["operationArgs"]):
|
|
1038
|
+
variableStr += operation["operationArgs"][varName]["requestStr"]
|
|
1039
|
+
operationAry = operationName.split(".")
|
|
1040
|
+
operationType = operationAry.pop(0)
|
|
1041
|
+
queryStr = operationType + " "
|
|
1042
|
+
queryStr += renderCamelCase(".".join(operationAry))
|
|
1043
|
+
queryStr += " ( " + variableStr + ") {\n"
|
|
1044
|
+
queryStr += indent + operation["name"] + " ( "
|
|
1045
|
+
for argName in operation["args"]:
|
|
1046
|
+
arg = operation["args"][argName]
|
|
1047
|
+
if arg["varName"] in variablesObj:
|
|
1048
|
+
queryStr += arg["responseStr"]
|
|
1049
|
+
queryStr += ") {\n" + renderArgsAndFields("", variablesObj, operation, operation["type"]["definition"], " ") + " }"
|
|
1050
|
+
queryStr += indent + "\n}";
|
|
1051
|
+
body = {
|
|
1052
|
+
"query":queryStr,
|
|
1053
|
+
"variables":variablesObj,
|
|
1054
|
+
"operationName":renderCamelCase(".".join(operationAry)),
|
|
1055
|
+
}
|
|
1056
|
+
return body
|
|
1057
|
+
|
|
1058
|
+
def renderArgsAndFields(responseArgStr, variablesObj, curOperation, definition, indent):
|
|
1059
|
+
for fieldName in definition['fields']:
|
|
1060
|
+
field = definition['fields'][fieldName]
|
|
1061
|
+
field_name = field['alias'] if 'alias' in field else field['name']
|
|
1062
|
+
responseArgStr += indent + field_name
|
|
1063
|
+
if field.get("args") and not isinstance(field['args'], list):
|
|
1064
|
+
if (len(list(field['args'].keys()))>0):
|
|
1065
|
+
argsPresent = False
|
|
1066
|
+
argStr = " ( "
|
|
1067
|
+
for argName in field['args']:
|
|
1068
|
+
arg = field['args'][argName]
|
|
1069
|
+
if arg["varName"] in variablesObj:
|
|
1070
|
+
argStr += arg['responseStr'] + " "
|
|
1071
|
+
argsPresent = True
|
|
1072
|
+
argStr += ") "
|
|
1073
|
+
if argsPresent==True:
|
|
1074
|
+
responseArgStr += argStr
|
|
1075
|
+
if field.get("type") and field['type'].get('definition') and field['type']['definition']['fields'] is not None:
|
|
1076
|
+
responseArgStr += " {\n"
|
|
1077
|
+
for subfieldIndex in field['type']['definition']['fields']:
|
|
1078
|
+
subfield = field['type']['definition']['fields'][subfieldIndex]
|
|
1079
|
+
subfield_name = subfield['alias'] if 'alias' in subfield else subfield['name']
|
|
1080
|
+
responseArgStr += indent + " " + subfield_name
|
|
1081
|
+
if subfield.get("args") and len(list(subfield["args"].keys()))>0:
|
|
1082
|
+
argsPresent = False
|
|
1083
|
+
subArgStr = " ( "
|
|
1084
|
+
for argName in subfield['args']:
|
|
1085
|
+
arg = subfield['args'][argName]
|
|
1086
|
+
if arg["varName"] in variablesObj:
|
|
1087
|
+
argsPresent = True
|
|
1088
|
+
subArgStr += arg['responseStr'] + " "
|
|
1089
|
+
subArgStr += " )"
|
|
1090
|
+
if argsPresent==True:
|
|
1091
|
+
responseArgStr += subArgStr
|
|
1092
|
+
if subfield.get("type") and subfield['type'].get("definition") and (subfield['type']['definition'].get("fields") or subfield['type']['definition'].get('inputFields')):
|
|
1093
|
+
responseArgStr += " {\n"
|
|
1094
|
+
responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, subfield['type']['definition'], indent + " ")
|
|
1095
|
+
if subfield['type']['definition'].get('possibleTypes'):
|
|
1096
|
+
for possibleTypeName in subfield['type']['definition']['possibleTypes']:
|
|
1097
|
+
possibleType = subfield['type']['definition']['possibleTypes'][possibleTypeName]
|
|
1098
|
+
responseArgStr += indent + " ... on " + possibleType['name'] + " {\n"
|
|
1099
|
+
if possibleType.get('fields') or possibleType.get('inputFields'):
|
|
1100
|
+
responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, possibleType, indent + " ")
|
|
1101
|
+
responseArgStr += indent + " }\n"
|
|
1102
|
+
responseArgStr += indent + " }"
|
|
1103
|
+
elif subfield.get('type') and subfield['type'].get('definition') and subfield['type']['definition'].get('possibleTypes'):
|
|
1104
|
+
responseArgStr += " {\n"
|
|
1105
|
+
responseArgStr += indent + " __typename\n"
|
|
1106
|
+
for possibleTypeName in subfield['type']['definition']['possibleTypes']:
|
|
1107
|
+
possibleType = subfield['type']['definition']['possibleTypes'][possibleTypeName]
|
|
1108
|
+
responseArgStr += indent + " ... on " + possibleType['name'] + " {\n"
|
|
1109
|
+
if possibleType.get('fields') or possibleType.get('inputFields'):
|
|
1110
|
+
responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, possibleType, indent + " ")
|
|
1111
|
+
responseArgStr += indent + " }\n"
|
|
1112
|
+
responseArgStr += indent + " }\n"
|
|
1113
|
+
responseArgStr += "\n"
|
|
1114
|
+
if field['type']['definition'].get('possibleTypes'):
|
|
1115
|
+
for possibleTypeName in field['type']['definition']['possibleTypes']:
|
|
1116
|
+
possibleType = field['type']['definition']['possibleTypes'][possibleTypeName]
|
|
1117
|
+
responseArgStr += indent + " ... on " + possibleType['name'] + " {\n"
|
|
1118
|
+
if possibleType.get('fields') or possibleType.get('inputFields'):
|
|
1119
|
+
responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, possibleType, indent + " ")
|
|
1120
|
+
responseArgStr += indent + " }\n"
|
|
1121
|
+
responseArgStr += indent + "}\n"
|
|
1122
|
+
if field.get('type') and field['type'].get('definition') and field['type']['definition'].get('inputFields'):
|
|
1123
|
+
responseArgStr += " {\n"
|
|
1124
|
+
for subfieldName in field['type']['definition'].get('inputFields'):
|
|
1125
|
+
subfield = field['type']['definition']['inputFields'][subfieldName]
|
|
1126
|
+
subfield_name = subfield['alias'] if 'alias' in subfield else subfield['name']
|
|
1127
|
+
responseArgStr += indent + " " + subfield_name
|
|
1128
|
+
if subfield.get('type') and subfield['type'].get('definition') and (subfield['type']['definition'].get('fields') or subfield['type']['definition'].get('inputFields')):
|
|
1129
|
+
responseArgStr += " {\n"
|
|
1130
|
+
responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, subfield['type']['definition'], indent + " ")
|
|
1131
|
+
responseArgStr += indent + " }\n"
|
|
1132
|
+
if field['type']['definition'].get('possibleTypes'):
|
|
1133
|
+
for possibleTypeName in field['type']['definition']['possibleTypes']:
|
|
1134
|
+
possibleType = field['type']['definition']['possibleTypes'][possibleTypeName]
|
|
1135
|
+
responseArgStr += indent + "... on " + possibleType['name'] + " {\n"
|
|
1136
|
+
if possibleType.get('fields') or possibleType.get('inputFields'):
|
|
1137
|
+
responseArgStr = renderArgsAndFields(responseArgStr, variablesObj, curOperation, possibleType, indent + " ")
|
|
1138
|
+
responseArgStr += indent + " }\n"
|
|
1139
|
+
responseArgStr += indent + "}\n"
|
|
1140
|
+
responseArgStr += "\n"
|
|
1141
|
+
return responseArgStr
|