1
- """Index and retrive information from the resource JSON."""
2
- import jmespath
1
+ """Index and retrive information from the resource JSON.
2
+
3
+ The classes are organized as follows:
4
+
5
+ * ResourceIndexBuilder - Takes a boto3 resource and converts into the
6
+ index format we need to do server side completions.
7
+ * CompleterQuery - Takes the index from ResourceIndexBuilder and looks
8
+ up how to perform the autocompletion. Note that this class does
9
+ *not* actually do the autocompletion. It merely tells you how
10
+ you _would_ do the autocompletion if you made the appropriate
11
+ service calls.
12
+ * ServerSideCompleter - The thing that does the actual autocompletion.
13
+ You tell it the command/operation/param you're on, and it will
14
+ return a list of completions for you.
15
+
16
+ """
17
+ import logging
3
18
from collections import namedtuple
4
19
20
+ import jmespath
21
+ from botocore import xform_name
22
+
23
+ LOG = logging .getLogger (__name__ )
24
+
5
25
# service - The name of the AWS service
6
26
# operation - The name of the AWS operation
7
27
# params - A dict of params to send in the request (not implemented yet)
@@ -90,7 +110,9 @@ def describe_autocomplete(self, service, operation, param):
90
110
91
111
"""
92
112
service_index = self ._index [service ]
113
+ LOG .debug (service_index )
93
114
if param not in service_index .get ('operations' , {}).get (operation , {}):
115
+ LOG .debug ("param not in index: %s" , param )
94
116
return None
95
117
p = service_index ['operations' ][operation ][param ]
96
118
resource_name = p ['resourceName' ]
@@ -103,6 +125,69 @@ def describe_autocomplete(self, service, operation, param):
103
125
params = {}, path = path )
104
126
105
127
128
+ class ServerSideCompleter (object ):
129
+ def __init__ (self , session , builder ):
130
+ # session is a boto3 session.
131
+ # It is a public attribute as it is intended to be
132
+ # changed if the profile changes.
133
+ self .session = session
134
+ self ._loader = session ._loader
135
+ self ._builder = builder
136
+ self ._client_cache = {}
137
+ self ._completer_cache = {}
138
+
139
+ def _get_completer_for_service (self , service_name , resource_model ):
140
+ if service_name not in self ._completer_cache :
141
+ index = self ._builder .build_index (resource_model )
142
+ cq = CompleterQuery ({service_name : index })
143
+ self ._completer_cache [service_name ] = cq
144
+ return self ._completer_cache [service_name ]
145
+
146
+ def _get_client (self , service_name ):
147
+ if service_name in self ._client_cache :
148
+ return self ._client_cache [service_name ]
149
+ client = self .session .client (service_name )
150
+ self ._client_cache [service_name ] = client
151
+ return client
152
+
153
+ def autocomplete (self , service , operation , param ):
154
+ # Example call:
155
+ # service='ec2', operation='terminate-instances',
156
+ # param='--instance-ids'.
157
+ # We need to convert this to botocore syntax.
158
+ # First try to load the resource model.
159
+ LOG .debug ("Called with: %s, %s, %s" , service , operation , param )
160
+ try :
161
+ resource_model = self ._loader .load_service_model (
162
+ service , 'resources-1' )
163
+ except Exception as e :
164
+ # No resource == no server side completion.
165
+ return
166
+ # Now convert operation to the name used by botocore.
167
+ client = self ._get_client (service )
168
+ api_operation_name = client .meta .method_to_api_mapping .get (
169
+ operation .replace ('-' , '_' ))
170
+ if api_operation_name is None :
171
+ return
172
+ # Now we need to convert the param name to the
173
+ # casing used by the API.
174
+ completer = self ._get_completer_for_service (service ,
175
+ resource_model )
176
+ result = completer .describe_autocomplete (
177
+ service , api_operation_name , param )
178
+ if result is None :
179
+ return
180
+ # DEBUG:awsshell.resource.index:RESULTS:
181
+ # ServerCompletion(service=u'ec2', operation=u'DescribeInstances',
182
+ # params={}, path=u'Reservations[].Instances[].InstanceId')
183
+ try :
184
+ response = getattr (client , xform_name (result .operation , '_' ))()
185
+ except Exception as e :
186
+ return
187
+ results = jmespath .search (result .path , response )
188
+ return results
189
+
190
+
106
191
def main ():
107
192
import sys
108
193
import json
0 commit comments