# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import copy
+from copy import copy
import logging
import os
import posixpath
+from data_source import DataSource
from environment import IsPreviewServer
from extensions_paths import JSON_TEMPLATES, PRIVATE_TEMPLATES
+from file_system import FileNotFoundError
+from future import Future, Collect
import third_party.json_schema_compiler.json_parse as json_parse
import third_party.json_schema_compiler.model as model
from environment import IsPreviewServer
'''
def __init__(self,
- api_name,
- api_models,
- ref_resolver,
- disable_refs,
+ namespace,
availability_finder,
json_cache,
template_cache,
features_bundle,
event_byname_function):
- self._ref_resolver = ref_resolver
- self._disable_refs = disable_refs
self._availability_finder = availability_finder
self._api_availabilities = json_cache.GetFromFile(
posixpath.join(JSON_TEMPLATES, 'api_availabilities.json'))
self._api_features = features_bundle.GetAPIFeatures()
self._template_cache = template_cache
self._event_byname_function = event_byname_function
- self._namespace = api_models.GetModel(api_name).Get()
-
- def _FormatDescription(self, description):
- if self._disable_refs:
- return description
- return self._ref_resolver.ResolveAllLinks(description,
- namespace=self._namespace.name)
+ self._namespace = namespace
def _GetLink(self, link):
- if self._disable_refs:
- type_name = link.split('.', 1)[-1]
- return { 'href': '#type-%s' % type_name, 'text': link, 'name': link }
- return self._ref_resolver.SafeGetLink(link, namespace=self._namespace.name)
+ ref = link if '.' in link else (self._namespace.name + '.' + link)
+ return { 'ref': ref, 'text': link, 'name': link }
def ToDict(self):
if self._namespace is None:
'events': self._GenerateEvents(self._namespace.events),
'domEvents': self._GenerateDomEvents(self._namespace.events),
'properties': self._GenerateProperties(self._namespace.properties),
+ 'introList': self._GetIntroTableList(),
+ 'channelWarning': self._GetChannelWarning(),
}
if self._namespace.deprecated:
as_dict['deprecated'] = self._namespace.deprecated
- # Rendering the intro list is really expensive and there's no point doing it
- # unless we're rending the page - and disable_refs=True implies we're not.
- if not self._disable_refs:
- as_dict.update({
- 'introList': self._GetIntroTableList(),
- 'channelWarning': self._GetChannelWarning(),
- })
+
as_dict['byName'] = _GetByNameDict(as_dict)
return as_dict
def _GetChannelWarning(self):
if not self._IsExperimental():
- return { self._GetApiAvailability().channel: True }
+ return { self._GetApiAvailability().channel_info.channel: True }
return None
def _IsExperimental(self):
def _GenerateType(self, type_):
type_dict = {
'name': type_.simple_name,
- 'description': self._FormatDescription(type_.description),
+ 'description': type_.description,
'properties': self._GenerateProperties(type_.properties),
'functions': self._GenerateFunctions(type_.functions),
'events': self._GenerateEvents(type_.events),
def _GenerateFunction(self, function):
function_dict = {
'name': function.simple_name,
- 'description': self._FormatDescription(function.description),
+ 'description': function.description,
'callback': self._GenerateCallback(function.callback),
'parameters': [],
'returns': None,
def _GenerateEvent(self, event):
event_dict = {
'name': event.simple_name,
- 'description': self._FormatDescription(event.description),
+ 'description': event.description,
'filters': [self._GenerateProperty(f) for f in event.filters],
'conditions': [self._GetLink(condition)
for condition in event.conditions],
}
self._AddCommonProperties(event_dict, event)
# Add the Event members to each event in this object.
- # If refs are disabled then don't worry about this, since it's only needed
- # for rendering, and disable_refs=True implies we're not rendering.
- if self._event_byname_function and not self._disable_refs:
+ if self._event_byname_function:
event_dict['byName'].update(self._event_byname_function())
# We need to create the method description for addListener based on the
# information stored in |event|.
property_dict = {
'name': property_.simple_name,
'optional': property_.optional,
- 'description': self._FormatDescription(property_.description),
+ 'description': property_.description,
'properties': self._GenerateProperties(type_.properties),
'functions': self._GenerateFunctions(type_.functions),
'parameters': [],
def _GenerateCallbackProperty(self, callback):
property_dict = {
'name': callback.simple_name,
- 'description': self._FormatDescription(callback.description),
+ 'description': callback.description,
'optional': callback.optional,
'is_callback': True,
'id': _CreateId(callback, 'property'),
return {
'title': 'Description',
'content': [
- { 'text': self._FormatDescription(self._namespace.description) }
+ { 'text': self._namespace.description }
]
}
if self._IsExperimental():
status = 'experimental'
version = None
+ scheduled = None
else:
availability = self._GetApiAvailability()
- status = availability.channel
- version = availability.version
+ status = availability.channel_info.channel
+ version = availability.channel_info.version
+ scheduled = availability.scheduled
return {
'title': 'Availability',
'content': [{
posixpath.join(PRIVATE_TEMPLATES,
'intro_tables',
'%s_message.html' % status)).Get(),
- 'version': version
+ 'version': version,
+ 'scheduled': scheduled
}]
}
if table_info is None:
return misc_rows
- for category in table_info.keys():
- content = copy.deepcopy(table_info[category])
- for node in content:
+ for category in table_info.iterkeys():
+ content = []
+ for node in table_info[category]:
# If there is a 'partial' argument and it hasn't already been
# converted to a Handlebar object, transform it to a template.
if 'partial' in node:
+ # Note: it's enough to copy() not deepcopy() because only a single
+ # top-level key is being modified.
+ node = copy(node)
node['partial'] = self._template_cache.GetFromFile(
posixpath.join(PRIVATE_TEMPLATES, node['partial'])).Get()
+ content.append(node)
misc_rows.append({ 'title': category, 'content': content })
return misc_rows
def _AddCommonProperties(self, target, src):
if src.deprecated is not None:
- target['deprecated'] = self._FormatDescription(
- src.deprecated)
+ target['deprecated'] = src.deprecated
if (src.parent is not None and
not isinstance(src.parent, model.Namespace)):
target['parentName'] = src.parent.simple_name
return self._samples.FilterSamples(key, self._api_name)
-class APIDataSource(object):
+class APIDataSource(DataSource):
'''This class fetches and loads JSON APIs from the FileSystem passed in with
|compiled_fs_factory|, so the APIs can be plugged into templates.
'''
-
- class Factory(object):
- def __init__(self,
- compiled_fs_factory,
- file_system,
- availability_finder,
- api_models,
- features_bundle,
- object_store_creator):
- self._json_cache = compiled_fs_factory.ForJson(file_system)
- self._template_cache = compiled_fs_factory.ForTemplates(file_system)
- self._availability_finder = availability_finder
- self._api_models = api_models
- self._features_bundle = features_bundle
- self._model_cache_refs = object_store_creator.Create(
- APIDataSource, 'model-cache-refs')
- self._model_cache_no_refs = object_store_creator.Create(
- APIDataSource, 'model-cache-no-refs')
-
- # These must be set later via the SetFooDataSourceFactory methods.
- self._ref_resolver_factory = None
- self._samples_data_source_factory = None
-
- # This caches the result of _LoadEventByName.
- self._event_byname = None
-
- def SetSamplesDataSourceFactory(self, samples_data_source_factory):
- self._samples_data_source_factory = samples_data_source_factory
-
- def SetReferenceResolverFactory(self, ref_resolver_factory):
- self._ref_resolver_factory = ref_resolver_factory
-
- def Create(self, request):
- '''Creates an APIDataSource.
- '''
- if self._samples_data_source_factory is None:
- # Only error if there is a request, which means this APIDataSource is
- # actually being used to render a page.
- if request is not None:
- logging.error('SamplesDataSource.Factory was never set in '
- 'APIDataSource.Factory.')
- samples = None
- else:
- samples = self._samples_data_source_factory.Create(request)
- return APIDataSource(self._GetSchemaModel, samples)
-
- def _LoadEventByName(self):
- '''All events have some members in common. We source their description
- from Event in events.json.
- '''
- if self._event_byname is None:
- self._event_byname = _GetEventByNameFromEvents(
- self._GetSchemaModel('events', True))
- return self._event_byname
-
- def _GetModelCache(self, disable_refs):
- if disable_refs:
- return self._model_cache_no_refs
- return self._model_cache_refs
-
- def _GetSchemaModel(self, api_name, disable_refs):
- jsc_model = self._GetModelCache(disable_refs).Get(api_name).Get()
- if jsc_model is not None:
- return jsc_model
-
- jsc_model = _JSCModel(
- api_name,
- self._api_models,
- self._ref_resolver_factory.Create() if not disable_refs else None,
- disable_refs,
- self._availability_finder,
- self._json_cache,
- self._template_cache,
- self._features_bundle,
- self._LoadEventByName).ToDict()
-
- self._GetModelCache(disable_refs).Set(api_name, jsc_model)
+ def __init__(self, server_instance, request):
+ file_system = server_instance.host_file_system_provider.GetTrunk()
+ self._json_cache = server_instance.compiled_fs_factory.ForJson(file_system)
+ self._template_cache = server_instance.compiled_fs_factory.ForTemplates(
+ file_system)
+ self._availability_finder = server_instance.availability_finder
+ self._api_models = server_instance.api_models
+ self._features_bundle = server_instance.features_bundle
+ self._model_cache = server_instance.object_store_creator.Create(
+ APIDataSource)
+
+ # This caches the result of _LoadEventByName.
+ self._event_byname = None
+ self._samples = server_instance.samples_data_source_factory.Create(request)
+
+ def _LoadEventByName(self):
+ '''All events have some members in common. We source their description
+ from Event in events.json.
+ '''
+ if self._event_byname is None:
+ self._event_byname = _GetEventByNameFromEvents(
+ self._GetSchemaModel('events').Get())
+ return self._event_byname
+
+ def _GetSchemaModel(self, api_name):
+ jsc_model_future = self._model_cache.Get(api_name)
+ model_future = self._api_models.GetModel(api_name)
+ def resolve():
+ jsc_model = jsc_model_future.Get()
+ if jsc_model is None:
+ jsc_model = _JSCModel(
+ model_future.Get(),
+ self._availability_finder,
+ self._json_cache,
+ self._template_cache,
+ self._features_bundle,
+ self._LoadEventByName).ToDict()
+ self._model_cache.Set(api_name, jsc_model)
return jsc_model
-
- def __init__(self, get_schema_model, samples):
- self._get_schema_model = get_schema_model
- self._samples = samples
-
- def _GenerateHandlebarContext(self, handlebar_dict):
- # Parsing samples on the preview server takes seconds and doesn't add
- # anything. Don't do it.
- if not IsPreviewServer():
- handlebar_dict['samples'] = _LazySamplesGetter(
- handlebar_dict['name'],
- self._samples)
- return handlebar_dict
-
- def get(self, api_name, disable_refs=False):
- return self._GenerateHandlebarContext(
- self._get_schema_model(api_name, disable_refs))
+ return Future(callback=resolve)
+
+ def _GetImpl(self, api_name):
+ handlebar_dict_future = self._GetSchemaModel(api_name)
+ def resolve():
+ handlebar_dict = handlebar_dict_future.Get()
+ # Parsing samples on the preview server takes seconds and doesn't add
+ # anything. Don't do it.
+ if not IsPreviewServer():
+ handlebar_dict['samples'] = _LazySamplesGetter(
+ handlebar_dict['name'],
+ self._samples)
+ return handlebar_dict
+ return Future(callback=resolve)
+
+ def get(self, api_name):
+ return self._GetImpl(api_name).Get()
+
+ def Cron(self):
+ futures = [self._GetImpl(name) for name in self._api_models.GetNames()]
+ return Collect(futures, except_pass=FileNotFoundError)