Chromium Code Reviews| Index: chrome/common/extensions/docs/server2/api_data_source.py |
| diff --git a/chrome/common/extensions/docs/server2/api_data_source.py b/chrome/common/extensions/docs/server2/api_data_source.py |
| index f81dbf2eee63e7b7100b0d15605bed44cc994e22..dcb445ae397e78413103c5fd6f932526569ea285 100644 |
| --- a/chrome/common/extensions/docs/server2/api_data_source.py |
| +++ b/chrome/common/extensions/docs/server2/api_data_source.py |
| @@ -65,6 +65,124 @@ def _GetEventByNameFromEvents(events): |
| return _GetByNameDict(event_list[0]) |
| +class _APINodeCursor(object): |
| + '''An abstract representation of a node in an APISchemaGraph. |
| + The current position in the graph is represented by a path into the |
| + underlying dictionary. So if the APISchemaGraph is: |
| + |
| + { |
| + 'tabs': { |
| + 'types': { |
| + 'Tab': { |
| + 'properties': { |
| + 'url': { |
| + ... |
| + } |
| + } |
| + } |
| + } |
| + } |
| + } |
| + |
| + then the 'url' property would be represented by: |
| + |
| + ['tabs', 'types', 'Tab', 'properties', 'url'] |
| + ''' |
| + def __init__(self, availability_finder, namespace_name): |
| + # The cursor begins life at the root. |
| + self._lookup_path = [namespace_name] |
| + self._node_availabilities = availability_finder.GetAPINodeAvailability( |
| + namespace_name) |
| + self._namespace_name = namespace_name |
| + |
| + def _GetParentPath(self): |
| + '''Returns the path pointing to this node's parent. |
| + ''' |
| + assert len(self._lookup_path) > 2, \ |
| + 'Tried to look up parent for the top-level node.' |
| + |
| + # lookup_path[-1] is the name of the current node. |
| + # lookup_path[-2] is this node's category (e.g. types, events, etc.). |
| + # Thus, the parent node is described by lookup_path[:-2]. |
| + return self._lookup_path[:-2] |
| + |
| + def _LookupNodeAvailability(self): |
| + '''Returns the ChannelInfo object for this node. |
| + ''' |
| + return self._node_availabilities.Lookup(*self._lookup_path).annotation |
| + |
| + def _LookupParentNodeAvailability(self): |
| + '''Returns the ChannelInfo object for this node's parent. |
| + ''' |
| + return self._node_availabilities.Lookup(*self._GetParentPath()).annotation |
| + |
| + def _CheckNamespacePrefix(self): |
| + '''API schemas may prepend the namespace name to top-level types |
| + (e.g. declarativeWebRequest > types > declarativeWebRequest.IgnoreRules), |
| + but just the base name (here, 'IgnoreRules') will be in the |lookup_path|. |
| + Try creating an alternate |lookup_path| by adding the namespace name. |
| + ''' |
| + # lookup_path[0] is always the API namespace, and |
| + # lookup_path[1] is always the node category (e.g. types, functions, etc.). |
| + # Thus, lookup_path[2] is always the top-level node name. |
| + base_name = self._lookup_path[2] |
| + self._lookup_path[2] = '%s.%s' % (self._namespace_name, base_name) |
| + node_availability = self._LookupNodeAvailability() |
| + if node_availability is not None: |
| + return node_availability |
| + # We want to maintain a working lookup_path, so only restore it |
| + # if modifying the lookup_path did not work. |
| + self._lookup_path[2] = base_name |
| + return None |
| + |
| + def _CheckEventCallback(self): |
| + '''Within API schemas, an event has a list of 'properties' that the event's |
| + callback expects. The callback itself is not explicitly represented in the |
| + schema. However, when creating an event node in _JSCModel, a callback node |
| + is generated and acts as the parent for the event's properties. |
| + Modify |lookup_path| to check the original schema format. |
| + ''' |
| + if 'events' in self._lookup_path and 'callback' in self._lookup_path: |
| + self._lookup_path.remove('callback') |
|
ahernandez
2014/06/27 03:00:01
I'm not sure if this should be restored or not.
|
| + return self._LookupNodeAvailability() |
| + return None |
| + |
| + def GetAvailability(self): |
| + '''Returns availability information for this node. |
| + ''' |
| + if self._lookup_path[0] != self._namespace_name: |
| + # |lookup_path| won't be lookup up if it doesn't start with the API name. |
| + return None |
| + |
| + for lookup in (self._LookupNodeAvailability, |
| + self._CheckEventCallback, |
| + self._CheckNamespacePrefix): |
| + node_availability = lookup() |
| + if node_availability is not None: |
| + break |
| + |
| + if node_availability is None: |
| + logging.warning('No availability found for: %s' % ' > '.join( |
| + self._lookup_path)) |
| + return None |
| + |
| + # Only render this node's availability if it differs from the parent |
| + # node's availability. |
| + if node_availability == self._LookupParentNodeAvailability(): |
| + return None |
| + return node_availability |
| + |
| + def DescendTo(self, *path): |
| + '''Moves down the APISchemaGraph, following |path|. |
| + ''' |
| + self._lookup_path.extend(path) |
| + |
| + def Ascend(self): |
| + '''Moves to the parent node. |
| + ''' |
| + self._lookup_path = self._GetParentPath() |
| + |
| + |
| class _JSCModel(object): |
| '''Uses a Model from the JSON Schema Compiler and generates a dict that |
| a Handlebar template can use for a data source. |
| @@ -72,12 +190,13 @@ class _JSCModel(object): |
| def __init__(self, |
| namespace, |
| - availability, |
| + availability_finder, |
| json_cache, |
| template_cache, |
| features_bundle, |
| event_byname_future): |
| - self._availability = availability |
| + self._availability = availability_finder.GetAPIAvailability(namespace.name) |
| + self._current_node = _APINodeCursor(availability_finder, namespace.name) |
| self._api_availabilities = json_cache.GetFromFile( |
| posixpath.join(JSON_TEMPLATES, 'api_availabilities.json')) |
| self._intro_tables = json_cache.GetFromFile( |
| @@ -126,18 +245,23 @@ class _JSCModel(object): |
| def _IsExperimental(self): |
| return self._namespace.name.startswith('experimental') |
| - def _GenerateTypes(self, types): |
| - return [self._GenerateType(t) for t in types] |
| + def _GenerateTypes(self, types, no_gen=False): |
| + return [self._GenerateType(t, no_gen=no_gen) for t in types] |
| - def _GenerateType(self, type_): |
| + def _GenerateType(self, type_, no_gen=False): |
| + '''If |no_gen| is True, don't generate availability info. |
| + ''' |
| + self._current_node.DescendTo('types', type_.simple_name) |
| type_dict = { |
| 'name': type_.simple_name, |
| 'description': type_.description, |
| 'properties': self._GenerateProperties(type_.properties), |
| 'functions': self._GenerateFunctions(type_.functions), |
| 'events': self._GenerateEvents(type_.events), |
| - 'id': _CreateId(type_, 'type') |
| + 'id': _CreateId(type_, 'type'), |
| + 'availability': None if no_gen else self._GetAvailabilityTemplate() |
| } |
| + self._current_node.Ascend() |
|
ahernandez
2014/06/27 03:00:01
I'm not sure if the whole purpose of the APINodeCu
|
| self._RenderTypeInformation(type_, type_dict) |
| return type_dict |
| @@ -155,7 +279,8 @@ class _JSCModel(object): |
| } |
| self._AddCommonProperties(function_dict, function) |
| if function.returns: |
| - function_dict['returns'] = self._GenerateType(function.returns) |
| + function_dict['returns'] = self._GenerateType(function.returns, |
| + no_gen=True) |
|
ahernandez
2014/06/27 03:00:01
I think I now understand what the deal with the bl
|
| for param in function.params: |
| function_dict['parameters'].append(self._GenerateProperty(param)) |
| if function.callback is not None: |
| @@ -266,7 +391,8 @@ class _JSCModel(object): |
| for param in function.params: |
| property_dict['parameters'].append(self._GenerateProperty(param)) |
| if function.returns: |
| - property_dict['returns'] = self._GenerateType(function.returns) |
| + property_dict['returns'] = self._GenerateType(function.returns, |
| + no_gen=True) |
| value = property_.value |
| if value is not None: |
| @@ -296,7 +422,7 @@ class _JSCModel(object): |
| def _RenderTypeInformation(self, type_, dst_dict): |
| dst_dict['is_object'] = type_.property_type == model.PropertyType.OBJECT |
| if type_.property_type == model.PropertyType.CHOICES: |
| - dst_dict['choices'] = self._GenerateTypes(type_.choices) |
| + dst_dict['choices'] = self._GenerateTypes(type_.choices, no_gen=True) |
| # We keep track of which == last for knowing when to add "or" between |
| # choices in templates. |
| if len(dst_dict['choices']) > 0: |
| @@ -304,7 +430,7 @@ class _JSCModel(object): |
| elif type_.property_type == model.PropertyType.REF: |
| dst_dict['link'] = self._GetLink(type_.ref_type) |
| elif type_.property_type == model.PropertyType.ARRAY: |
| - dst_dict['array'] = self._GenerateType(type_.item_type) |
| + dst_dict['array'] = self._GenerateType(type_.item_type, no_gen=True) |
| elif type_.property_type == model.PropertyType.ENUM: |
| dst_dict['enum_values'] = [ |
| {'name': value.name, 'description': value.description} |
| @@ -336,6 +462,23 @@ class _JSCModel(object): |
| return intro_rows |
| + def _GetAvailabilityTemplate(self, status=None, version=None, scheduled=None): |
| + '''Returns an object that the templates use to display availability |
| + information. |
| + ''' |
| + if status is None: |
| + availability_info = self._current_node.GetAvailability() |
| + if availability_info is None: |
| + return None |
| + status = availability_info.channel |
| + version = availability_info.version |
| + return { |
| + 'partial': self._template_cache.GetFromFile( |
| + '%sintro_tables/%s_message.html' % (PRIVATE_TEMPLATES, status)).Get(), |
| + 'version': version, |
| + 'scheduled': scheduled |
| + } |
| + |
| def _GetIntroDescriptionRow(self): |
| ''' Generates the 'Description' row data for an API intro table. |
| ''' |
| @@ -359,14 +502,11 @@ class _JSCModel(object): |
| scheduled = self._availability.scheduled |
| return { |
| 'title': 'Availability', |
| - 'content': [{ |
| - 'partial': self._template_cache.GetFromFile( |
| - posixpath.join(PRIVATE_TEMPLATES, |
| - 'intro_tables', |
| - '%s_message.html' % status)).Get(), |
| - 'version': version, |
| - 'scheduled': scheduled |
| - }] |
| + 'content': [ |
| + self._GetAvailabilityTemplate(status=status, |
| + version=version, |
| + scheduled=scheduled) |
| + ] |
| } |
| def _GetIntroDependencyRows(self): |
| @@ -506,8 +646,7 @@ class APIDataSource(DataSource): |
| if jsc_model is None: |
| jsc_model = _JSCModel( |
| model_future.Get(), |
| - self._platform_bundle.GetAvailabilityFinder( |
| - platform).GetAPIAvailability(api_name), |
| + self._platform_bundle.GetAvailabilityFinder(platform), |
| self._json_cache, |
| self._template_cache, |
| self._platform_bundle.GetFeaturesBundle(platform), |