# docs.py

#

Copyright (C) 2011 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Subclass for the Discovery portion of GoogleCL which

#

manages the documentation

n charge of saving/loading the API directory, nd retrieves and stores the Discovery documents.

    import httplib2
    import logging

    import simplejson as json
    import googlecl

    LOG = logging.getLogger(googlecl.LOGGER_NAME)
    apis_path = googlecl.get_data_path('apis.dat', create_missing_dir=True)
    SERVICE_BLACKLIST = ['latitude']
    LIST_URL = '%s/discovery/v1/apis?preferred=true&pp=0'
    SERVICE_URL = '%s/discovery/v1/apis/%s/%s/rest'

#

    class DocManager():

#

      def __init__(self, local, base_url):
        self.base_url = base_url
        self.load()
        self.apis = {}
        self.local = local
        if self.local:
          if isinstance(self.local, list): # local comes from the config file
            for filename in self.local:    # Be sure to give the correct path.
              self.loadDoc(filename)
          else:
            self.loadDoc(self.local)

#

Loads the currently saved list of preferred APIs,

      def load(self, force=False):

#

or downloads the latest version. Can be forced with the command 'refresh apis'

rgs: force: If true, the will always download a new document

        try:
          if force:
            raise
          f = open(apis_path, 'r')
          self.directory = json.load(f)
        except:
          http = httplib2.Http()
          resp, content = http.request(LIST_URL % self.base_url)
          self.directory = json.loads(content)

#

Removes blacklisted APIs (currently just latitude)

          self.directory['items'] = [a for a in self.directory['items']
                                     if a['name'] not in SERVICE_BLACKLIST]
          f = open(apis_path, 'w')
          json.dump(self.directory, f, indent=2)
        if hasattr(self, 'local') and self.local:
          if isinstance(self.local, list): # local comes from the config file
            for filename in self.local:    # Be sure to give the correct path.
              self.loadDoc(filename)
          else:
            self.loadDoc(self.local)

#

Loads a discovery document stored locally

      def loadDoc(self, filename):

#

rgs: filename: The file being loaded

        try:
          doc = json.loads(file(filename).read())
        except:
          LOG.info('Failed to load ' + filename)
          return
        self.apis[doc['name']+'.'+doc['version']] = doc
        info = {'name': doc['name'], 'version': doc['version']}
        self.directory['items'].append(info)

#

Parses through arguments to determine service, version, and gets docs

      def run(self, argv, isHelp, verbose):

#

lso prints help, if applicable

rgs: argv: The arguments which are passed in isHelp: If help should be displayed verbose: If isHelp, then whether it should be verbose eturns: If it doesn't display help, then a tuple of the service name, version, documentation, and remaining args

        http = httplib2.Http()

#

Parses the service name and version If no version is defined, finds the currently preferred one

        servicename = argv[0]
        if len(argv) > 1:
          version = argv[1]
        else:
          version = None
        args = argv[2:]
        if not version or not version[0] == 'v' or not version[1].isdigit():
          version = None
          for api in self.directory['items']:
            if api['name'] == servicename:
              version = api['version']
              args = argv[1:]
              break
          if not version:
            LOG.error('Did not recognize service.')
            return

#

Fetches documentation for service

        if servicename + '.' + version in self.apis:
          doc = self.apis[servicename + '.' + version]
        else:
          resp, content = http.request(SERVICE_URL % (self.base_url, servicename, version))
          doc = json.loads(content)
          self.apis[servicename + '.' + version] = doc

        if 'error' in doc:
          LOG.error('Did not recognize version.')
          return

#

Displays help, if requested

        if isHelp:
          help(doc, verbose, *args)
          return

        return servicename, version, doc, args

#

Prints the help for an arbitrary service

    def help(doc, verbose, *path):

#

rgs: doc: Discovery document for the service verbose: Whether or not all information should be displayed path: The path to the desired method, parameter, or other attribute

#

Locates the desired object Will try to follow path implicitly (for resources, methods, parameters) otherwise the path must be fully defined (most likely useful for schemas)

      base = doc
      for p in path:
        if p[:2] == '--':
          p = p[2:]
        if p in base:
          base = base[p]
        elif 'resources' in base and p in base['resources']:
          base = base['resources'][p]
        elif 'methods' in base and p in base['methods']:
          base = base['methods'][p]
        elif 'parameters' in base and p in base['parameters']:
          base = base['parameters'][p]
        else:
          n = ('resources' in base) + ('methods' in base) + ('parameters' in base
                                                             and not base == doc)
          while (n == 1 or len(base) == 1) and not p in base:
            i = 0
            if 'resources' in base: i = base.keys().index('resources')
            elif 'methods' in base: i = base.keys().index('methods')
            elif 'parameters' in base: i = base.keys().index('parameters')
            base = base[base.keys()[i]]
            n = ('resources' in base) + ('methods' in base) + ('parameters' in base)
          if p in base:
            base = base[p]
          else:
            LOG.error('Error in path: "' + p + '" not found')
            return

#

Displays the attributes of the requested object Formatted if object is base API, method, or resource and not verbose.

      if not verbose:
        if isinstance(base, dict):
          if 'version' in base: #Base API
            print ' ' + base['description']
            print '  Resources: ' + ', '.join(base['resources'])
          elif 'httpMethod' in base: #Method
            print ' ' + base['description']
            if 'parameterOrder' in base:
              print '  Requires: ' + ' AND '.join(base['parameterOrder']) \
              + ' Optional: ' + ', '.join([i for i in base['parameters'] if \
              i not in base['parameterOrder']])
            elif 'parameters' in base:
              print '  Optional: ' + ', '.join(base['parameters'])
            if 'request' in base:
              print '  Request schema: ' + base['request']['$ref']
          elif 'methods' in base or 'resources' in base: #Resource
            if 'methods' in base:
              print '  Methods: ' + ', '.join(base['methods'])
            if 'resources' in base:
              print '  Resources: ' + ', '.join(base['resources'])
          else: #Everything else
            for obj in base:
              if isinstance(base[obj], dict) or isinstance(base[obj], list):
                print '  ' + obj + ": " + ', '.join(base[obj])
              else:
                print '  ' + obj + ": " + str(base[obj])
        else:
          print base
      else:
        if isinstance(base, dict):
          for obj in base:
            if isinstance(base[obj], dict) or isinstance(base[obj], list):
              print '  ' + obj + ": " + ', '.join(base[obj])
            else:
              print '  ' + obj + ": " + str(base[obj])
        else:
          print base