1# Copyright (c) 2012 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
5import logging
6
7from data_source import DataSource
8from docs_server_utils import StringIdentity
9from environment import IsPreviewServer
10from file_system import FileNotFoundError
11from future import Future, All
12from jsc_view import JSCView, GetEventByNameFromEvents
13from platform_util import GetPlatforms
14from samples_data_source import CreateSamplesView
15
16
17class APIDataSource(DataSource):
18  '''This class fetches and loads JSON APIs from the FileSystem passed in with
19  |compiled_fs_factory|, so the APIs can be plugged into templates.
20  '''
21  def __init__(self, server_instance, request):
22    file_system = server_instance.host_file_system_provider.GetMaster()
23    self._json_cache = server_instance.compiled_fs_factory.ForJson(file_system)
24    self._template_cache = server_instance.compiled_fs_factory.ForTemplates(
25        file_system)
26    self._platform_bundle = server_instance.platform_bundle
27    self._view_cache = server_instance.object_store_creator.Create(
28        APIDataSource,
29        # Update the models when any of templates, APIs, or Features change.
30        category=StringIdentity(self._json_cache.GetIdentity(),
31                                self._template_cache.GetIdentity(),
32                                self._platform_bundle.GetIdentity()))
33
34    # This caches the result of _LoadEventByName.
35    self._event_byname_futures = {}
36    self._request = request
37
38  def _LoadEventByName(self, platform):
39    '''All events have some members in common. We source their description
40    from Event in events.json.
41    '''
42    if platform not in self._event_byname_futures:
43      future = self._GetSchemaView(platform, 'events')
44      self._event_byname_futures[platform] = Future(
45          callback=lambda: GetEventByNameFromEvents(future.Get()))
46    return self._event_byname_futures[platform]
47
48  def _GetSchemaView(self, platform, api_name):
49    object_store_key = '/'.join((platform, api_name))
50    api_models = self._platform_bundle.GetAPIModels(platform)
51    jsc_view_future = self._view_cache.Get(object_store_key)
52    model_future = api_models.GetModel(api_name)
53    content_script_apis_future = api_models.GetContentScriptAPIs()
54    def resolve():
55      jsc_view = jsc_view_future.Get()
56      if jsc_view is None:
57        jsc_view = JSCView(
58            content_script_apis_future.Get(),
59            model_future.Get(),
60            self._platform_bundle.GetAvailabilityFinder(platform),
61            self._json_cache,
62            self._template_cache,
63            self._platform_bundle.GetFeaturesBundle(platform),
64            self._LoadEventByName(platform),
65            platform).ToDict()
66        self._view_cache.Set(object_store_key, jsc_view)
67      return jsc_view
68    return Future(callback=resolve)
69
70  def _GetImpl(self, platform, api_name):
71    jsc_view_future = self._GetSchemaView(platform, api_name)
72    def resolve():
73      jsc_view = jsc_view_future.Get()
74      # Parsing samples on the preview server takes seconds and doesn't add
75      # anything. Don't do it.
76      if not IsPreviewServer():
77        samples_model = self._platform_bundle.GetSamplesModel(platform)
78        # Creates an object that lazily gets samples.
79        jsc_view['samples'] = type('getter', (object,), {
80          'get': lambda _, platform: CreateSamplesView(
81              samples_model.FilterSamples(jsc_view['name']), self._request)
82          })()
83      return jsc_view
84    return Future(callback=resolve)
85
86  def get(self, platform):
87    '''Return a getter object so that templates can perform lookups such
88    as apis.extensions.runtime.
89    '''
90    getter = lambda: 0
91    getter.get = lambda api_name: self._GetImpl(platform, api_name).Get()
92    return getter
93
94  def GetRefreshPaths(self):
95    tasks = []
96    for platform in GetPlatforms():
97      tasks += ['%s/%s' % (platform, api)
98                for api in
99                    self._platform_bundle.GetAPIModels(platform).GetNames()]
100    return tasks
101
102  def Refresh(self, path):
103    platform, api = path.split('/')
104    logging.info('Refreshing %s/%s' % (platform, api))
105    future = self._GetImpl(platform, api)
106    return All([future], except_pass=FileNotFoundError)
107