/src/googlecl/discovery/docs.py
Python | 221 lines | 183 code | 6 blank | 32 comment | 9 complexity | 0192ff44ddc0cd5adfbf6dc712275682 MD5 | raw file
1# Copyright (C) 2011 Google Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" Subclass for the Discovery portion of GoogleCL which 16 manages the documentation 17 18In charge of saving/loading the API directory, 19and retrieves and stores the Discovery documents. 20""" 21 22import httplib2 23import logging 24 25import simplejson as json 26import googlecl 27 28LOG = logging.getLogger(googlecl.LOGGER_NAME) 29apis_path = googlecl.get_data_path('apis.dat', create_missing_dir=True) 30SERVICE_BLACKLIST = ['latitude'] 31LIST_URL = '%s/discovery/v1/apis?preferred=true&pp=0' 32SERVICE_URL = '%s/discovery/v1/apis/%s/%s/rest' 33 34class DocManager(): 35 def __init__(self, local, base_url): 36 self.base_url = base_url 37 self.load() 38 self.apis = {} 39 self.local = local 40 if self.local: 41 if isinstance(self.local, list): # local comes from the config file 42 for filename in self.local: # Be sure to give the correct path. 43 self.loadDoc(filename) 44 else: 45 self.loadDoc(self.local) 46 47 def load(self, force=False): 48 """ Loads the currently saved list of preferred APIs, 49 or downloads the latest version. 50 Can be forced with the command 'refresh apis' 51 52 Args: 53 force: If true, the will always download a new document 54 """ 55 try: 56 if force: 57 raise 58 f = open(apis_path, 'r') 59 self.directory = json.load(f) 60 except: 61 http = httplib2.Http() 62 resp, content = http.request(LIST_URL % self.base_url) 63 self.directory = json.loads(content) 64 # Removes blacklisted APIs (currently just latitude) 65 self.directory['items'] = [a for a in self.directory['items'] 66 if a['name'] not in SERVICE_BLACKLIST] 67 f = open(apis_path, 'w') 68 json.dump(self.directory, f, indent=2) 69 if hasattr(self, 'local') and self.local: 70 if isinstance(self.local, list): # local comes from the config file 71 for filename in self.local: # Be sure to give the correct path. 72 self.loadDoc(filename) 73 else: 74 self.loadDoc(self.local) 75 76 def loadDoc(self, filename): 77 """ Loads a discovery document stored locally 78 79 Args: 80 filename: The file being loaded 81 """ 82 try: 83 doc = json.loads(file(filename).read()) 84 except: 85 LOG.info('Failed to load ' + filename) 86 return 87 self.apis[doc['name']+'.'+doc['version']] = doc 88 info = {'name': doc['name'], 'version': doc['version']} 89 self.directory['items'].append(info) 90 91 def run(self, argv, isHelp, verbose): 92 """ Parses through arguments to determine service, version, and gets docs 93 Also prints help, if applicable 94 95 Args: 96 argv: The arguments which are passed in 97 isHelp: If help should be displayed 98 verbose: If isHelp, then whether it should be verbose 99 Returns: 100 If it doesn't display help, then a tuple of the service name, 101 version, documentation, and remaining args 102 """ 103 http = httplib2.Http() 104 105 # Parses the service name and version 106 # If no version is defined, finds the currently preferred one 107 servicename = argv[0] 108 if len(argv) > 1: 109 version = argv[1] 110 else: 111 version = None 112 args = argv[2:] 113 if not version or not version[0] == 'v' or not version[1].isdigit(): 114 version = None 115 for api in self.directory['items']: 116 if api['name'] == servicename: 117 version = api['version'] 118 args = argv[1:] 119 break 120 if not version: 121 LOG.error('Did not recognize service.') 122 return 123 124 # Fetches documentation for service 125 if servicename + '.' + version in self.apis: 126 doc = self.apis[servicename + '.' + version] 127 else: 128 resp, content = http.request(SERVICE_URL % (self.base_url, servicename, version)) 129 doc = json.loads(content) 130 self.apis[servicename + '.' + version] = doc 131 132 if 'error' in doc: 133 LOG.error('Did not recognize version.') 134 return 135 136 # Displays help, if requested 137 if isHelp: 138 help(doc, verbose, *args) 139 return 140 141 return servicename, version, doc, args 142 143def help(doc, verbose, *path): 144 """ Prints the help for an arbitrary service 145 146 Args: 147 doc: Discovery document for the service 148 verbose: Whether or not all information should be displayed 149 path: The path to the desired method, parameter, or other attribute 150 """ 151 152 # Locates the desired object 153 # Will try to follow path implicitly (for resources, methods, parameters) 154 # otherwise the path must be fully defined (most likely useful for schemas) 155 base = doc 156 for p in path: 157 if p[:2] == '--': 158 p = p[2:] 159 if p in base: 160 base = base[p] 161 elif 'resources' in base and p in base['resources']: 162 base = base['resources'][p] 163 elif 'methods' in base and p in base['methods']: 164 base = base['methods'][p] 165 elif 'parameters' in base and p in base['parameters']: 166 base = base['parameters'][p] 167 else: 168 n = ('resources' in base) + ('methods' in base) + ('parameters' in base 169 and not base == doc) 170 while (n == 1 or len(base) == 1) and not p in base: 171 i = 0 172 if 'resources' in base: i = base.keys().index('resources') 173 elif 'methods' in base: i = base.keys().index('methods') 174 elif 'parameters' in base: i = base.keys().index('parameters') 175 base = base[base.keys()[i]] 176 n = ('resources' in base) + ('methods' in base) + ('parameters' in base) 177 if p in base: 178 base = base[p] 179 else: 180 LOG.error('Error in path: "' + p + '" not found') 181 return 182 183 # Displays the attributes of the requested object 184 # Formatted if object is base API, method, or resource and not verbose. 185 if not verbose: 186 if isinstance(base, dict): 187 if 'version' in base: #Base API 188 print ' ' + base['description'] 189 print ' Resources: ' + ', '.join(base['resources']) 190 elif 'httpMethod' in base: #Method 191 print ' ' + base['description'] 192 if 'parameterOrder' in base: 193 print ' Requires: ' + ' AND '.join(base['parameterOrder']) \ 194 + ' Optional: ' + ', '.join([i for i in base['parameters'] if \ 195 i not in base['parameterOrder']]) 196 elif 'parameters' in base: 197 print ' Optional: ' + ', '.join(base['parameters']) 198 if 'request' in base: 199 print ' Request schema: ' + base['request']['$ref'] 200 elif 'methods' in base or 'resources' in base: #Resource 201 if 'methods' in base: 202 print ' Methods: ' + ', '.join(base['methods']) 203 if 'resources' in base: 204 print ' Resources: ' + ', '.join(base['resources']) 205 else: #Everything else 206 for obj in base: 207 if isinstance(base[obj], dict) or isinstance(base[obj], list): 208 print ' ' + obj + ": " + ', '.join(base[obj]) 209 else: 210 print ' ' + obj + ": " + str(base[obj]) 211 else: 212 print base 213 else: 214 if isinstance(base, dict): 215 for obj in base: 216 if isinstance(base[obj], dict) or isinstance(base[obj], list): 217 print ' ' + obj + ": " + ', '.join(base[obj]) 218 else: 219 print ' ' + obj + ": " + str(base[obj]) 220 else: 221 print base