Docserver: Factor SamplesModel out of SamplesDataSource

BUG=275039
NOTRY=True

Review URL: https://codereview.chromium.org/437323003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288149 0039d316-1c4b-4281-b951-d872f2087c98
parent 17748983
...@@ -9,19 +9,7 @@ from file_system import FileNotFoundError ...@@ -9,19 +9,7 @@ from file_system import FileNotFoundError
from future import Future, All from future import Future, All
from jsc_view import JSCView, GetEventByNameFromEvents from jsc_view import JSCView, GetEventByNameFromEvents
from platform_util import GetPlatforms from platform_util import GetPlatforms
from samples_data_source import CreateSamplesView
class _LazySamplesGetter(object):
'''This class is needed so that an extensions API page does not have to fetch
the apps samples page and vice versa.
'''
def __init__(self, api_name, samples):
self._api_name = api_name
self._samples = samples
def get(self, key):
return self._samples.FilterSamples(key, self._api_name)
class APIDataSource(DataSource): class APIDataSource(DataSource):
...@@ -43,7 +31,7 @@ class APIDataSource(DataSource): ...@@ -43,7 +31,7 @@ class APIDataSource(DataSource):
# This caches the result of _LoadEventByName. # This caches the result of _LoadEventByName.
self._event_byname_futures = {} self._event_byname_futures = {}
self._samples = server_instance.samples_data_source_factory.Create(request) self._request = request
def _LoadEventByName(self, platform): def _LoadEventByName(self, platform):
'''All events have some members in common. We source their description '''All events have some members in common. We source their description
...@@ -84,9 +72,12 @@ class APIDataSource(DataSource): ...@@ -84,9 +72,12 @@ class APIDataSource(DataSource):
# Parsing samples on the preview server takes seconds and doesn't add # Parsing samples on the preview server takes seconds and doesn't add
# anything. Don't do it. # anything. Don't do it.
if not IsPreviewServer(): if not IsPreviewServer():
jsc_view['samples'] = _LazySamplesGetter( samples_model = self._platform_bundle.GetSamplesModel(platform)
jsc_view['name'], # Creates an object that lazily gets samples.
self._samples) jsc_view['samples'] = type('getter', (object,), {
'get': lambda _, platform: CreateSamplesView(
samples_model.FilterSamples(jsc_view['name']), self._request)
})()
return jsc_view return jsc_view
return Future(callback=resolve) return Future(callback=resolve)
......
application: chrome-apps-doc application: chrome-apps-doc
version: 3-39-1 version: 3-39-2
runtime: python27 runtime: python27
api_version: 1 api_version: 1
threadsafe: false threadsafe: false
......
...@@ -7,7 +7,7 @@ import sys ...@@ -7,7 +7,7 @@ import sys
from file_system import FileSystem, StatInfo, FileNotFoundError from file_system import FileSystem, StatInfo, FileNotFoundError
from future import Future from future import Future
from path_util import IsDirectory from path_util import IsDirectory, ToDirectory
from third_party.json_schema_compiler.memoize import memoize from third_party.json_schema_compiler.memoize import memoize
...@@ -43,8 +43,7 @@ class CachingFileSystem(FileSystem): ...@@ -43,8 +43,7 @@ class CachingFileSystem(FileSystem):
# Always stat the parent directory, since it will have the stat of the child # Always stat the parent directory, since it will have the stat of the child
# anyway, and this gives us an entire directory's stat info at once. # anyway, and this gives us an entire directory's stat info at once.
dir_path, file_path = posixpath.split(path) dir_path, file_path = posixpath.split(path)
if dir_path and not dir_path.endswith('/'): dir_path = ToDirectory(dir_path)
dir_path += '/'
def make_stat_info(dir_stat): def make_stat_info(dir_stat):
'''Converts a dir stat into the correct resulting StatInfo; if the Stat '''Converts a dir stat into the correct resulting StatInfo; if the Stat
......
...@@ -6,6 +6,7 @@ from compiled_file_system import CompiledFileSystem ...@@ -6,6 +6,7 @@ from compiled_file_system import CompiledFileSystem
from docs_server_utils import StringIdentity from docs_server_utils import StringIdentity
from file_system import FileNotFoundError from file_system import FileNotFoundError
from future import Future from future import Future
from path_util import ToDirectory
class ChainedCompiledFileSystem(object): class ChainedCompiledFileSystem(object):
...@@ -52,8 +53,7 @@ class ChainedCompiledFileSystem(object): ...@@ -52,8 +53,7 @@ class ChainedCompiledFileSystem(object):
lambda compiled_fs: compiled_fs.GetFileVersion(path)) lambda compiled_fs: compiled_fs.GetFileVersion(path))
def GetFromFileListing(self, path): def GetFromFileListing(self, path):
if not path.endswith('/'): path = ToDirectory(path)
path += '/'
return self._GetImpl( return self._GetImpl(
path, path,
lambda compiled_fs: compiled_fs.GetFromFileListing(path), lambda compiled_fs: compiled_fs.GetFromFileListing(path),
......
...@@ -8,7 +8,7 @@ import schema_util ...@@ -8,7 +8,7 @@ import schema_util
from docs_server_utils import ToUnicode from docs_server_utils import ToUnicode
from file_system import FileNotFoundError from file_system import FileNotFoundError
from future import Future from future import Future
from path_util import AssertIsDirectory, AssertIsFile from path_util import AssertIsDirectory, AssertIsFile, ToDirectory
from third_party.handlebar import Handlebar from third_party.handlebar import Handlebar
from third_party.json_schema_compiler import json_parse from third_party.json_schema_compiler import json_parse
from third_party.json_schema_compiler.memoize import memoize from third_party.json_schema_compiler.memoize import memoize
...@@ -236,8 +236,7 @@ class CompiledFileSystem(object): ...@@ -236,8 +236,7 @@ class CompiledFileSystem(object):
return self._file_system.Stat(path).version return self._file_system.Stat(path).version
def GetFileListingVersion(self, path): def GetFileListingVersion(self, path):
if not path.endswith('/'): path = ToDirectory(path)
path += '/'
cache_entry = self._list_object_store.Get(path).Get() cache_entry = self._list_object_store.Get(path).Get()
if cache_entry is not None: if cache_entry is not None:
return cache_entry.version return cache_entry.version
......
...@@ -2,4 +2,4 @@ cron: ...@@ -2,4 +2,4 @@ cron:
- description: Repopulates all cached data. - description: Repopulates all cached data.
url: /_cron url: /_cron
schedule: every 5 minutes schedule: every 5 minutes
target: 3-39-1 target: 3-39-2
...@@ -7,6 +7,7 @@ from api_list_data_source import APIListDataSource ...@@ -7,6 +7,7 @@ from api_list_data_source import APIListDataSource
from data_source import DataSource from data_source import DataSource
from manifest_data_source import ManifestDataSource from manifest_data_source import ManifestDataSource
from permissions_data_source import PermissionsDataSource from permissions_data_source import PermissionsDataSource
from samples_data_source import SamplesDataSource
from sidenav_data_source import SidenavDataSource from sidenav_data_source import SidenavDataSource
from strings_data_source import StringsDataSource from strings_data_source import StringsDataSource
from template_data_source import ( from template_data_source import (
...@@ -22,6 +23,7 @@ _all_data_sources = { ...@@ -22,6 +23,7 @@ _all_data_sources = {
'manifest_source': ManifestDataSource, 'manifest_source': ManifestDataSource,
'partials': PartialDataSource, 'partials': PartialDataSource,
'permissions': PermissionsDataSource, 'permissions': PermissionsDataSource,
'samples': SamplesDataSource,
'sidenavs': SidenavDataSource, 'sidenavs': SidenavDataSource,
'strings': StringsDataSource, 'strings': StringsDataSource,
'whatsNew' : WhatsNewDataSource 'whatsNew' : WhatsNewDataSource
......
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
from api_categorizer import APICategorizer from api_categorizer import APICategorizer
from api_models import APIModels from api_models import APIModels
from availability_finder import AvailabilityFinder from availability_finder import AvailabilityFinder
from empty_dir_file_system import EmptyDirFileSystem
from environment import IsDevServer
from features_bundle import FeaturesBundle from features_bundle import FeaturesBundle
from future import All from future import All
from platform_util import GetPlatforms, PlatformToExtensionType from platform_util import GetPlatforms, PlatformToExtensionType
from reference_resolver import ReferenceResolver from reference_resolver import ReferenceResolver
from samples_model import SamplesModel
class _PlatformData(object): class _PlatformData(object):
...@@ -18,6 +21,7 @@ class _PlatformData(object): ...@@ -18,6 +21,7 @@ class _PlatformData(object):
self.reference_resolver = None self.reference_resolver = None
self.availability_finder = None self.availability_finder = None
self.api_categorizer = None self.api_categorizer = None
self.samples_model = None
class PlatformBundle(object): class PlatformBundle(object):
...@@ -28,14 +32,38 @@ class PlatformBundle(object): ...@@ -28,14 +32,38 @@ class PlatformBundle(object):
compiled_fs_factory, compiled_fs_factory,
host_fs_at_trunk, host_fs_at_trunk,
host_file_system_iterator, host_file_system_iterator,
object_store_creator): object_store_creator,
base_path):
self._branch_utility = branch_utility self._branch_utility = branch_utility
self._compiled_fs_factory = compiled_fs_factory self._compiled_fs_factory = compiled_fs_factory
self._host_fs_at_trunk = host_fs_at_trunk self._host_fs_at_trunk = host_fs_at_trunk
self._host_file_system_iterator = host_file_system_iterator self._host_file_system_iterator = host_file_system_iterator
self._object_store_creator = object_store_creator self._object_store_creator = object_store_creator
self._base_path = base_path
self._platform_data = dict((p, _PlatformData()) for p in GetPlatforms()) self._platform_data = dict((p, _PlatformData()) for p in GetPlatforms())
def GetSamplesModel(self, platform):
if self._platform_data[platform].samples_model is None:
# Note: samples are super slow in the dev server because it doesn't
# support async fetch, so disable them.
if IsDevServer():
extension_samples_fs = EmptyDirFileSystem()
app_samples_fs = EmptyDirFileSystem()
else:
extension_samples_fs = self._host_fs_at_trunk
# TODO(kalman): Re-enable the apps samples, see http://crbug.com/344097.
app_samples_fs = EmptyDirFileSystem()
#app_samples_fs = github_file_system_provider.Create(
# 'GoogleChrome', 'chrome-app-samples')
self._platform_data[platform].samples_model = SamplesModel(
extension_samples_fs,
app_samples_fs,
self._compiled_fs_factory,
self.GetReferenceResolver(platform),
self._base_path,
platform)
return self._platform_data[platform].samples_model
def GetFeaturesBundle(self, platform): def GetFeaturesBundle(self, platform):
if self._platform_data[platform].features_bundle is None: if self._platform_data[platform].features_bundle is None:
self._platform_data[platform].features_bundle = FeaturesBundle( self._platform_data[platform].features_bundle = FeaturesBundle(
......
...@@ -2,219 +2,30 @@ ...@@ -2,219 +2,30 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import json
import logging import logging
import posixpath
import re
import traceback import traceback
from data_source import DataSource
from extensions_paths import EXAMPLES from extensions_paths import EXAMPLES
import third_party.json_schema_compiler.json_comment_eater as json_comment_eater from future import All, Future
import url_constants from platform_util import GetPlatforms
_DEFAULT_ICON_PATH = 'images/sample-default-icon.png' def _GetSampleId(sample_name):
class SamplesDataSource(object):
'''Constructs a list of samples and their respective files and api calls.
'''
class Factory(object):
'''A factory to create SamplesDataSource instances bound to individual
Requests.
'''
def __init__(self,
host_file_system,
app_samples_file_system,
compiled_fs_factory,
platform_bundle,
base_path):
self._host_file_system = host_file_system
self._app_samples_file_system = app_samples_file_system
self._platform_bundle = platform_bundle
self._base_path = base_path
self._extensions_cache = compiled_fs_factory.Create(
host_file_system,
self._MakeSamplesList,
SamplesDataSource,
category='extensions')
self._extensions_text_cache = compiled_fs_factory.ForUnicode(
host_file_system)
self._apps_cache = compiled_fs_factory.Create(
app_samples_file_system,
lambda *args: self._MakeSamplesList(*args, is_apps=True),
SamplesDataSource,
category='apps')
self._apps_text_cache = compiled_fs_factory.ForUnicode(
app_samples_file_system)
def Create(self, request):
'''Returns a new SamplesDataSource bound to |request|.
'''
return SamplesDataSource(self._extensions_cache,
self._apps_cache,
self._base_path,
request)
def _GetAPIItems(self, js_file):
chrome_pattern = r'chrome[\w.]+'
# Add API calls that appear normally, like "chrome.runtime.connect".
calls = set(re.findall(chrome_pattern, js_file))
# Add API calls that have been assigned into variables, like
# "var storageArea = chrome.storage.sync; storageArea.get", which should
# be expanded like "chrome.storage.sync.get".
for match in re.finditer(r'var\s+(\w+)\s*=\s*(%s);' % chrome_pattern,
js_file):
var_name, api_prefix = match.groups()
for var_match in re.finditer(r'\b%s\.([\w.]+)\b' % re.escape(var_name),
js_file):
api_suffix, = var_match.groups()
calls.add('%s.%s' % (api_prefix, api_suffix))
return calls
def _GetDataFromManifest(self, path, text_cache, file_system):
manifest = text_cache.GetFromFile(path + '/manifest.json').Get()
try:
manifest_json = json.loads(json_comment_eater.Nom(manifest))
except ValueError as e:
logging.error('Error parsing manifest.json for %s: %s' % (path, e))
return None
l10n_data = {
'name': manifest_json.get('name', ''),
'description': manifest_json.get('description', None),
'icon': manifest_json.get('icons', {}).get('128', None),
'default_locale': manifest_json.get('default_locale', None),
'locales': {}
}
if not l10n_data['default_locale']:
return l10n_data
locales_path = path + '/_locales/'
locales_dir = file_system.ReadSingle(locales_path).Get()
if locales_dir:
def load_locale_json(path):
return (path, json.loads(text_cache.GetFromFile(path).Get()))
try:
locales_json = [load_locale_json(locales_path + f + 'messages.json')
for f in locales_dir]
except ValueError as e:
logging.error('Error parsing locales files for %s: %s' % (path, e))
else:
for path, json_ in locales_json:
l10n_data['locales'][path[len(locales_path):].split('/')[0]] = json_
return l10n_data
def _MakeSamplesList(self, base_path, files, is_apps=False):
file_system = (self._app_samples_file_system if is_apps else
self._host_file_system)
text_cache = (self._apps_text_cache if is_apps else
self._extensions_text_cache)
samples_list = []
for filename in sorted(files):
if filename.rsplit('/')[-1] != 'manifest.json':
continue
# This is a little hacky, but it makes a sample page.
sample_path = filename.rsplit('/', 1)[-2]
sample_files = [path for path in files
if path.startswith(sample_path + '/')]
js_files = [path for path in sample_files if path.endswith('.js')]
js_contents = [text_cache.GetFromFile(
posixpath.join(base_path, js_file)).Get()
for js_file in js_files]
api_items = set()
for js in js_contents:
api_items.update(self._GetAPIItems(js))
api_calls = []
for item in sorted(api_items):
if len(item.split('.')) < 3:
continue
if item.endswith('.removeListener') or item.endswith('.hasListener'):
continue
if item.endswith('.addListener'):
item = item[:-len('.addListener')]
if item.startswith('chrome.'):
item = item[len('chrome.'):]
ref_data = self._platform_bundle.GetReferenceResolver(
'apps' if is_apps else 'extensions').GetLink(item)
# TODO(kalman): What about references like chrome.storage.sync.get?
# That should link to either chrome.storage.sync or
# chrome.storage.StorageArea.get (or probably both).
# TODO(kalman): Filter out API-only references? This can happen when
# the API namespace is assigned to a variable, but it's very hard to
# to disambiguate.
if ref_data is None:
continue
api_calls.append({
'name': ref_data['text'],
'link': ref_data['href']
})
if is_apps:
url = url_constants.GITHUB_BASE + '/' + sample_path
icon_base = url_constants.RAW_GITHUB_BASE + '/' + sample_path
download_url = url
else:
extension_sample_path = posixpath.join('examples', sample_path)
url = extension_sample_path
icon_base = extension_sample_path
download_url = extension_sample_path + '.zip'
manifest_data = self._GetDataFromManifest(
posixpath.join(base_path, sample_path),
text_cache,
file_system)
if manifest_data['icon'] is None:
icon_path = posixpath.join(
self._base_path, 'static', _DEFAULT_ICON_PATH)
else:
icon_path = '%s/%s' % (icon_base, manifest_data['icon'])
manifest_data.update({
'icon': icon_path,
'download_url': download_url,
'url': url,
'files': [f.replace(sample_path + '/', '') for f in sample_files],
'api_calls': api_calls
})
samples_list.append(manifest_data)
return samples_list
def __init__(self,
extensions_cache,
apps_cache,
base_path,
request):
self._extensions_cache = extensions_cache
self._apps_cache = apps_cache
self._base_path = base_path
self._request = request
def _GetSampleId(self, sample_name):
return sample_name.lower().replace(' ', '-') return sample_name.lower().replace(' ', '-')
def _GetAcceptedLanguages(self):
accept_language = self._request.headers.get('Accept-Language', None) def GetAcceptedLanguages(request):
if request is None:
return []
accept_language = request.headers.get('Accept-Language', None)
if accept_language is None: if accept_language is None:
return [] return []
return [lang_with_q.split(';')[0].strip() return [lang_with_q.split(';')[0].strip()
for lang_with_q in accept_language.split(',')] for lang_with_q in accept_language.split(',')]
def FilterSamples(self, key, api_name):
'''Fetches and filters the list of samples specified by |key|, returning
only the samples that use the API |api_name|. |key| is either 'apps' or
'extensions'.
'''
return [sample for sample in self.get(key) if any(
call['name'].startswith(api_name + '.')
for call in sample['api_calls'])]
def _CreateSamplesDict(self, key): def CreateSamplesView(samples_list, request):
if key == 'apps':
samples_list = self._apps_cache.GetFromFileListing('').Get()
else:
samples_list = self._extensions_cache.GetFromFileListing(EXAMPLES).Get()
return_list = [] return_list = []
for dict_ in samples_list: for dict_ in samples_list:
name = dict_['name'] name = dict_['name']
...@@ -228,26 +39,40 @@ class SamplesDataSource(object): ...@@ -228,26 +39,40 @@ class SamplesDataSource(object):
name_key = name[len('__MSG_'):-len('__')] name_key = name[len('__MSG_'):-len('__')]
description_key = description[len('__MSG_'):-len('__')] description_key = description[len('__MSG_'):-len('__')]
locale = sample_data['default_locale'] locale = sample_data['default_locale']
for lang in self._GetAcceptedLanguages(): for lang in GetAcceptedLanguages(request):
if lang in sample_data['locales']: if lang in sample_data['locales']:
locale = lang locale = lang
break break
locale_data = sample_data['locales'][locale] locale_data = sample_data['locales'][locale]
sample_data['name'] = locale_data[name_key]['message'] sample_data['name'] = locale_data[name_key]['message']
sample_data['description'] = locale_data[description_key]['message'] sample_data['description'] = locale_data[description_key]['message']
sample_data['id'] = self._GetSampleId(sample_data['name']) sample_data['id'] = _GetSampleId(sample_data['name'])
except Exception as e: except Exception:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
# Revert the sample to the original dict. # Revert the sample to the original dict.
sample_data = dict_ sample_data = dict_
return_list.append(sample_data) return_list.append(sample_data)
else: else:
dict_['id'] = self._GetSampleId(name) dict_['id'] = _GetSampleId(name)
return_list.append(dict_) return_list.append(dict_)
return return_list return return_list
def get(self, key):
return { class SamplesDataSource(DataSource):
'apps': lambda: self._CreateSamplesDict('apps'), '''Constructs a list of samples and their respective files and api calls.
'extensions': lambda: self._CreateSamplesDict('extensions') '''
}.get(key, lambda: {})() def __init__(self, server_instance, request):
self._platform_bundle = server_instance.platform_bundle
self._request = request
def _GetImpl(self, platform):
cache = self._platform_bundle.GetSamplesModel(platform).GetCache()
create_view = lambda samp_list: CreateSamplesView(samp_list, self._request)
return cache.GetFromFileListing('' if platform == 'apps'
else EXAMPLES).Then(create_view)
def get(self, platform):
return self._GetImpl(platform).Get()
def Cron(self):
return All([self._GetImpl(platform) for platform in GetPlatforms()])
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import os
import sys
import unittest
from samples_data_source import SamplesDataSource
from servlet import Request
from test_util import Server2Path
class SamplesDataSourceTest(unittest.TestCase):
def setUp(self):
self._base_path = Server2Path('test_data', 'samples_data_source')
def _ReadLocalFile(self, filename):
with open(os.path.join(self._base_path, filename), 'r') as f:
return f.read()
def _FakeGet(self, key):
return json.loads(self._ReadLocalFile(key))
def testFilterSamples(self):
sds = SamplesDataSource({}, {}, '.', Request.ForTest('/'))
sds.get = self._FakeGet
self.assertEquals(json.loads(self._ReadLocalFile('expected.json')),
sds.FilterSamples('samples.json', 'bobaloo'))
if __name__ == '__main__':
unittest.main()
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import logging
import posixpath
import re
from extensions_paths import EXAMPLES
from samples_data_source import SamplesDataSource
import third_party.json_schema_compiler.json_comment_eater as json_comment_eater
import url_constants
_DEFAULT_ICON_PATH = 'images/sample-default-icon.png'
def _GetAPIItems(js_file):
chrome_pattern = r'chrome[\w.]+'
# Add API calls that appear normally, like "chrome.runtime.connect".
calls = set(re.findall(chrome_pattern, js_file))
# Add API calls that have been assigned into variables, like
# "var storageArea = chrome.storage.sync; storageArea.get", which should
# be expanded like "chrome.storage.sync.get".
for match in re.finditer(r'var\s+(\w+)\s*=\s*(%s);' % chrome_pattern,
js_file):
var_name, api_prefix = match.groups()
for var_match in re.finditer(r'\b%s\.([\w.]+)\b' % re.escape(var_name),
js_file):
api_suffix, = var_match.groups()
calls.add('%s.%s' % (api_prefix, api_suffix))
return calls
class SamplesModel(object):
def __init__(self,
extension_samples_file_system,
app_samples_file_system,
compiled_fs_factory,
reference_resolver,
base_path,
platform):
self._samples_fs = (extension_samples_file_system if
platform == 'extensions' else app_samples_file_system)
self._samples_cache = compiled_fs_factory.Create(
self._samples_fs,
self._MakeSamplesList,
SamplesDataSource,
category=platform)
self._text_cache = compiled_fs_factory.ForUnicode(self._samples_fs)
self._reference_resolver = reference_resolver
self._base_path = base_path
self._platform = platform
def GetCache(self):
return self._samples_cache
def FilterSamples(self, api_name):
'''Fetches and filters the list of samples for this platform, returning
only the samples that use the API |api_name|.
'''
samples_list = self._samples_cache.GetFromFileListing(
'' if self._platform == 'apps' else EXAMPLES).Get()
return [sample for sample in samples_list if any(
call['name'].startswith(api_name + '.')
for call in sample['api_calls'])]
def _GetDataFromManifest(self, path, file_system):
manifest = self._text_cache.GetFromFile(path + '/manifest.json').Get()
try:
manifest_json = json.loads(json_comment_eater.Nom(manifest))
except ValueError as e:
logging.error('Error parsing manifest.json for %s: %s' % (path, e))
return None
l10n_data = {
'name': manifest_json.get('name', ''),
'description': manifest_json.get('description', None),
'icon': manifest_json.get('icons', {}).get('128', None),
'default_locale': manifest_json.get('default_locale', None),
'locales': {}
}
if not l10n_data['default_locale']:
return l10n_data
locales_path = path + '/_locales/'
locales_dir = file_system.ReadSingle(locales_path).Get()
if locales_dir:
def load_locale_json(path):
return (path, json.loads(self._text_cache.GetFromFile(path).Get()))
try:
locales_json = [load_locale_json(locales_path + f + 'messages.json')
for f in locales_dir]
except ValueError as e:
logging.error('Error parsing locales files for %s: %s' % (path, e))
else:
for path, json_ in locales_json:
l10n_data['locales'][path[len(locales_path):].split('/')[0]] = json_
return l10n_data
def _MakeSamplesList(self, base_path, files):
samples_list = []
for filename in sorted(files):
if filename.rsplit('/')[-1] != 'manifest.json':
continue
# This is a little hacky, but it makes a sample page.
sample_path = filename.rsplit('/', 1)[-2]
sample_files = [path for path in files
if path.startswith(sample_path + '/')]
js_files = [path for path in sample_files if path.endswith('.js')]
js_contents = [self._text_cache.GetFromFile(
posixpath.join(base_path, js_file)).Get()
for js_file in js_files]
api_items = set()
for js in js_contents:
api_items.update(_GetAPIItems(js))
api_calls = []
for item in sorted(api_items):
if len(item.split('.')) < 3:
continue
if item.endswith('.removeListener') or item.endswith('.hasListener'):
continue
if item.endswith('.addListener'):
item = item[:-len('.addListener')]
if item.startswith('chrome.'):
item = item[len('chrome.'):]
ref_data = self._reference_resolver.GetLink(item)
# TODO(kalman): What about references like chrome.storage.sync.get?
# That should link to either chrome.storage.sync or
# chrome.storage.StorageArea.get (or probably both).
# TODO(kalman): Filter out API-only references? This can happen when
# the API namespace is assigned to a variable, but it's very hard to
# to disambiguate.
if ref_data is None:
continue
api_calls.append({
'name': ref_data['text'],
'link': ref_data['href']
})
if self._platform == 'apps':
url = url_constants.GITHUB_BASE + '/' + sample_path
icon_base = url_constants.RAW_GITHUB_BASE + '/' + sample_path
download_url = url
else:
extension_sample_path = posixpath.join('examples', sample_path)
url = extension_sample_path
icon_base = extension_sample_path
download_url = extension_sample_path + '.zip'
manifest_data = self._GetDataFromManifest(
posixpath.join(base_path, sample_path),
self._samples_fs)
if manifest_data['icon'] is None:
icon_path = posixpath.join(
self._base_path, 'static', _DEFAULT_ICON_PATH)
else:
icon_path = '%s/%s' % (icon_base, manifest_data['icon'])
manifest_data.update({
'icon': icon_path,
'download_url': download_url,
'url': url,
'files': [f.replace(sample_path + '/', '') for f in sample_files],
'api_calls': api_calls
})
samples_list.append(manifest_data)
return samples_list
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import os
import unittest
from server_instance import ServerInstance
from test_file_system import TestFileSystem
from test_util import Server2Path
def _ReadLocalFile(filename):
base_path = Server2Path('test_data', 'samples_data_source')
with open(os.path.join(base_path, filename), 'r') as f:
return f.read()
class _FakeCache(object):
def __init__(self, obj):
self._cache = obj
def GetFromFileListing(self, _):
getter = lambda: 0
getter.Get = lambda: self._cache
return getter
class SamplesModelSourceTest(unittest.TestCase):
def setUp(self):
server_instance = ServerInstance.ForTest(file_system=TestFileSystem({}))
self._samples_model = server_instance.platform_bundle.GetSamplesModel(
'apps')
self._samples_model._samples_cache = _FakeCache(json.loads(_ReadLocalFile(
'samples.json')))
def testFilterSamples(self):
self.assertEquals(json.loads(_ReadLocalFile('expected.json')),
self._samples_model.FilterSamples('bobaloo'))
if __name__ == '__main__':
unittest.main()
...@@ -73,25 +73,8 @@ class ServerInstance(object): ...@@ -73,25 +73,8 @@ class ServerInstance(object):
self.compiled_fs_factory, self.compiled_fs_factory,
host_fs_at_trunk, host_fs_at_trunk,
self.host_file_system_iterator, self.host_file_system_iterator,
self.object_store_creator) self.object_store_creator,
self.base_path)
# Note: samples are super slow in the dev server because it doesn't support
# async fetch, so disable them.
if IsDevServer():
extension_samples_fs = EmptyDirFileSystem()
app_samples_fs = EmptyDirFileSystem()
else:
extension_samples_fs = host_fs_at_trunk
# TODO(kalman): Re-enable the apps samples, see http://crbug.com/344097.
app_samples_fs = EmptyDirFileSystem()
#app_samples_fs = github_file_system_provider.Create(
# 'GoogleChrome', 'chrome-app-samples')
self.samples_data_source_factory = SamplesDataSource.Factory(
extension_samples_fs,
app_samples_fs,
CompiledFileSystem.Factory(object_store_creator),
self.platform_bundle,
base_path)
self.content_providers = ContentProviders( self.content_providers = ContentProviders(
object_store_creator, object_store_creator,
......
...@@ -28,7 +28,7 @@ class TemplateRenderer(object): ...@@ -28,7 +28,7 @@ class TemplateRenderer(object):
rendering the template. rendering the template.
''' '''
assert isinstance(template, Handlebar), type(template) assert isinstance(template, Handlebar), type(template)
render_context = self._CreateDataSources(request) render_context = CreateDataSources(self._server_instance, request)
if data_sources is not None: if data_sources is not None:
render_context = dict((name, d) for name, d in render_context.iteritems() render_context = dict((name, d) for name, d in render_context.iteritems()
if name in data_sources) if name in data_sources)
...@@ -38,15 +38,6 @@ class TemplateRenderer(object): ...@@ -38,15 +38,6 @@ class TemplateRenderer(object):
'extensions_samples_url': EXTENSIONS_SAMPLES, 'extensions_samples_url': EXTENSIONS_SAMPLES,
'static': self._server_instance.base_path + 'static', 'static': self._server_instance.base_path + 'static',
}) })
if additional_context: render_context.update(additional_context or {})
render_context.update(additional_context)
render_data = template.Render(render_context) render_data = template.Render(render_context)
return render_data.text, render_data.errors return render_data.text, render_data.errors
def _CreateDataSources(self, request):
server_instance = self._server_instance
data_sources = CreateDataSources(server_instance, request=request)
data_sources.update({
'samples': server_instance.samples_data_source_factory.Create(request),
})
return data_sources
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment