Home | History | Annotate | Download | only in server2
      1 # Copyright 2013 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 from copy import deepcopy
      6 import json
      7 
      8 from data_source import DataSource
      9 from features_utility import Filtered
     10 from future import Future
     11 from manifest_features import ConvertDottedKeysToNested
     12 
     13 def _ListifyAndSortDocs(features, app_name):
     14   '''Convert a |feautres| dictionary, and all 'children' dictionaries, into
     15   lists recursively. Sort lists first by 'level' then by name.
     16   '''
     17   def sort_key(item):
     18     '''Key function to sort items primarily by level (according to index into
     19     levels) then subsort by name.
     20     '''
     21     levels = ('required', 'recommended', 'only_one', 'optional')
     22 
     23     return (levels.index(item.get('level', 'optional')), item['name'])
     24 
     25   def coerce_example_to_feature(feature):
     26     '''To display json in examples more clearly, convert the example of
     27     |feature| into the feature format, with a name and children, to be rendered
     28     by the templates. Only applicable to examples that are dictionaries.
     29     '''
     30     if not isinstance(feature.get('example'), dict):
     31       if 'example' in feature:
     32         feature['example'] = json.dumps(feature['example'])
     33       return
     34     # Add any keys/value pairs in the dict as children
     35     for key, value in feature['example'].iteritems():
     36       if not 'children' in feature:
     37         feature['children'] = {}
     38       feature['children'][key] = { 'name': key, 'example': value }
     39     del feature['example']
     40     del feature['has_example']
     41 
     42   def convert_and_sort(features):
     43     for key, value in features.items():
     44       if 'example' in value:
     45         value['has_example'] = True
     46         example = json.dumps(value['example'])
     47         if example == '{}':
     48           value['example'] = '{...}'
     49         elif example == '[]':
     50           value['example'] = '[...]'
     51         elif example == '[{}]':
     52           value['example'] = '[{...}]'
     53         else:
     54           coerce_example_to_feature(value)
     55       if 'children' in value:
     56         features[key]['children'] = convert_and_sort(value['children'])
     57     return sorted(features.values(), key=sort_key)
     58 
     59   # Replace {{platform}} in the 'name' manifest property example with
     60   # |app_name|, the convention that the normal template rendering uses.
     61   # TODO(kalman): Make the example a template and pass this through there.
     62   if 'name' in features:
     63     name = features['name']
     64     name['example'] = name['example'].replace('{{platform}}', app_name)
     65 
     66   features = convert_and_sort(features)
     67 
     68   return features
     69 
     70 def _AddLevelAnnotations(features):
     71   '''Add level annotations to |features|. |features| and children lists must be
     72   sorted by 'level'. Annotations are added to the first item in a group of
     73   features of the same 'level'.
     74 
     75   The last item in a list has 'is_last' set to True.
     76   '''
     77   annotations = {
     78     'required': 'Required',
     79     'recommended': 'Recommended',
     80     'only_one': 'Pick one (or none)',
     81     'optional': 'Optional'
     82   }
     83 
     84   def add_annotation(item, annotation):
     85     if not 'annotations' in item:
     86       item['annotations'] = []
     87     item['annotations'].insert(0, annotation)
     88 
     89   def annotate(parent_level, features):
     90     current_level = parent_level
     91     for item in features:
     92       level = item.get('level', 'optional')
     93       if level != current_level:
     94         add_annotation(item, annotations[level])
     95         current_level = level
     96       if 'children' in item:
     97         annotate(level, item['children'])
     98     if features:
     99       features[-1]['is_last'] = True
    100 
    101   annotate('required', features)
    102   return features
    103 
    104 class ManifestDataSource(DataSource):
    105   '''Provides access to the properties in manifest features.
    106   '''
    107   def __init__(self, server_instance, _):
    108     self._features_bundle = server_instance.features_bundle
    109     self._object_store = server_instance.object_store_creator.Create(
    110         ManifestDataSource)
    111 
    112   def _CreateManifestData(self):
    113     future_manifest_features = self._features_bundle.GetManifestFeatures()
    114     def resolve():
    115       manifest_features = future_manifest_features.Get()
    116       def for_templates(manifest_features, platform):
    117         return _AddLevelAnnotations(_ListifyAndSortDocs(
    118             ConvertDottedKeysToNested(
    119                 deepcopy(Filtered(manifest_features, platform + 's'))),
    120             app_name=platform.capitalize()))
    121       return {
    122         'apps': for_templates(manifest_features, 'app'),
    123         'extensions': for_templates(manifest_features, 'extension')
    124       }
    125     return Future(callback=resolve)
    126 
    127   def _GetCachedManifestData(self):
    128     data = self._object_store.Get('manifest_data').Get()
    129     if data is None:
    130       data = self._CreateManifestData().Get()
    131       self._object_store.Set('manifest_data', data)
    132     return data
    133 
    134   def Cron(self):
    135     return self._CreateManifestData()
    136 
    137   def get(self, key):
    138     return self._GetCachedManifestData().get(key)
    139