diff options
author | ahernandez.miralles@gmail.com <ahernandez.miralles@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-07 22:05:04 +0000 |
---|---|---|
committer | ahernandez.miralles@gmail.com <ahernandez.miralles@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-07 22:05:04 +0000 |
commit | 1d0e548a86cae4b6274fe9eef38b96a8e49aa12a (patch) | |
tree | b27dc9eb3319da3c9eaa93bb30493b6a6f63aac5 | |
parent | 177489835f6e988ff03548e579b50013785007e5 (diff) | |
download | chromium_src-1d0e548a86cae4b6274fe9eef38b96a8e49aa12a.zip chromium_src-1d0e548a86cae4b6274fe9eef38b96a8e49aa12a.tar.gz chromium_src-1d0e548a86cae4b6274fe9eef38b96a8e49aa12a.tar.bz2 |
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
14 files changed, 322 insertions, 324 deletions
diff --git a/chrome/common/extensions/docs/server2/api_data_source.py b/chrome/common/extensions/docs/server2/api_data_source.py index c57ecc5..56fae41 100644 --- a/chrome/common/extensions/docs/server2/api_data_source.py +++ b/chrome/common/extensions/docs/server2/api_data_source.py @@ -9,19 +9,7 @@ from file_system import FileNotFoundError from future import Future, All from jsc_view import JSCView, GetEventByNameFromEvents from platform_util import GetPlatforms - - -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) +from samples_data_source import CreateSamplesView class APIDataSource(DataSource): @@ -43,7 +31,7 @@ class APIDataSource(DataSource): # This caches the result of _LoadEventByName. self._event_byname_futures = {} - self._samples = server_instance.samples_data_source_factory.Create(request) + self._request = request def _LoadEventByName(self, platform): '''All events have some members in common. We source their description @@ -84,9 +72,12 @@ class APIDataSource(DataSource): # Parsing samples on the preview server takes seconds and doesn't add # anything. Don't do it. if not IsPreviewServer(): - jsc_view['samples'] = _LazySamplesGetter( - jsc_view['name'], - self._samples) + samples_model = self._platform_bundle.GetSamplesModel(platform) + # Creates an object that lazily gets samples. + jsc_view['samples'] = type('getter', (object,), { + 'get': lambda _, platform: CreateSamplesView( + samples_model.FilterSamples(jsc_view['name']), self._request) + })() return jsc_view return Future(callback=resolve) diff --git a/chrome/common/extensions/docs/server2/app.yaml b/chrome/common/extensions/docs/server2/app.yaml index b6ebba1..8398139 100644 --- a/chrome/common/extensions/docs/server2/app.yaml +++ b/chrome/common/extensions/docs/server2/app.yaml @@ -1,5 +1,5 @@ application: chrome-apps-doc -version: 3-39-1 +version: 3-39-2 runtime: python27 api_version: 1 threadsafe: false diff --git a/chrome/common/extensions/docs/server2/caching_file_system.py b/chrome/common/extensions/docs/server2/caching_file_system.py index c0ff5d0..15a81a36 100644 --- a/chrome/common/extensions/docs/server2/caching_file_system.py +++ b/chrome/common/extensions/docs/server2/caching_file_system.py @@ -7,7 +7,7 @@ import sys from file_system import FileSystem, StatInfo, FileNotFoundError from future import Future -from path_util import IsDirectory +from path_util import IsDirectory, ToDirectory from third_party.json_schema_compiler.memoize import memoize @@ -43,8 +43,7 @@ class CachingFileSystem(FileSystem): # 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. dir_path, file_path = posixpath.split(path) - if dir_path and not dir_path.endswith('/'): - dir_path += '/' + dir_path = ToDirectory(dir_path) def make_stat_info(dir_stat): '''Converts a dir stat into the correct resulting StatInfo; if the Stat diff --git a/chrome/common/extensions/docs/server2/chained_compiled_file_system.py b/chrome/common/extensions/docs/server2/chained_compiled_file_system.py index 6fbd31e..74e2a59 100644 --- a/chrome/common/extensions/docs/server2/chained_compiled_file_system.py +++ b/chrome/common/extensions/docs/server2/chained_compiled_file_system.py @@ -6,6 +6,7 @@ from compiled_file_system import CompiledFileSystem from docs_server_utils import StringIdentity from file_system import FileNotFoundError from future import Future +from path_util import ToDirectory class ChainedCompiledFileSystem(object): @@ -52,8 +53,7 @@ class ChainedCompiledFileSystem(object): lambda compiled_fs: compiled_fs.GetFileVersion(path)) def GetFromFileListing(self, path): - if not path.endswith('/'): - path += '/' + path = ToDirectory(path) return self._GetImpl( path, lambda compiled_fs: compiled_fs.GetFromFileListing(path), diff --git a/chrome/common/extensions/docs/server2/compiled_file_system.py b/chrome/common/extensions/docs/server2/compiled_file_system.py index 2f7769e..58ab967 100644 --- a/chrome/common/extensions/docs/server2/compiled_file_system.py +++ b/chrome/common/extensions/docs/server2/compiled_file_system.py @@ -8,7 +8,7 @@ import schema_util from docs_server_utils import ToUnicode from file_system import FileNotFoundError 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.json_schema_compiler import json_parse from third_party.json_schema_compiler.memoize import memoize @@ -236,8 +236,7 @@ class CompiledFileSystem(object): return self._file_system.Stat(path).version def GetFileListingVersion(self, path): - if not path.endswith('/'): - path += '/' + path = ToDirectory(path) cache_entry = self._list_object_store.Get(path).Get() if cache_entry is not None: return cache_entry.version diff --git a/chrome/common/extensions/docs/server2/cron.yaml b/chrome/common/extensions/docs/server2/cron.yaml index 4ac706a..f03c1d9 100644 --- a/chrome/common/extensions/docs/server2/cron.yaml +++ b/chrome/common/extensions/docs/server2/cron.yaml @@ -2,4 +2,4 @@ cron: - description: Repopulates all cached data. url: /_cron schedule: every 5 minutes - target: 3-39-1 + target: 3-39-2 diff --git a/chrome/common/extensions/docs/server2/data_source_registry.py b/chrome/common/extensions/docs/server2/data_source_registry.py index c864561..10d32df 100644 --- a/chrome/common/extensions/docs/server2/data_source_registry.py +++ b/chrome/common/extensions/docs/server2/data_source_registry.py @@ -7,6 +7,7 @@ from api_list_data_source import APIListDataSource from data_source import DataSource from manifest_data_source import ManifestDataSource from permissions_data_source import PermissionsDataSource +from samples_data_source import SamplesDataSource from sidenav_data_source import SidenavDataSource from strings_data_source import StringsDataSource from template_data_source import ( @@ -22,6 +23,7 @@ _all_data_sources = { 'manifest_source': ManifestDataSource, 'partials': PartialDataSource, 'permissions': PermissionsDataSource, + 'samples': SamplesDataSource, 'sidenavs': SidenavDataSource, 'strings': StringsDataSource, 'whatsNew' : WhatsNewDataSource diff --git a/chrome/common/extensions/docs/server2/platform_bundle.py b/chrome/common/extensions/docs/server2/platform_bundle.py index 77c7263..2c40608 100644 --- a/chrome/common/extensions/docs/server2/platform_bundle.py +++ b/chrome/common/extensions/docs/server2/platform_bundle.py @@ -5,10 +5,13 @@ from api_categorizer import APICategorizer from api_models import APIModels from availability_finder import AvailabilityFinder +from empty_dir_file_system import EmptyDirFileSystem +from environment import IsDevServer from features_bundle import FeaturesBundle from future import All from platform_util import GetPlatforms, PlatformToExtensionType from reference_resolver import ReferenceResolver +from samples_model import SamplesModel class _PlatformData(object): @@ -18,6 +21,7 @@ class _PlatformData(object): self.reference_resolver = None self.availability_finder = None self.api_categorizer = None + self.samples_model = None class PlatformBundle(object): @@ -28,14 +32,38 @@ class PlatformBundle(object): compiled_fs_factory, host_fs_at_trunk, host_file_system_iterator, - object_store_creator): + object_store_creator, + base_path): self._branch_utility = branch_utility self._compiled_fs_factory = compiled_fs_factory self._host_fs_at_trunk = host_fs_at_trunk self._host_file_system_iterator = host_file_system_iterator self._object_store_creator = object_store_creator + self._base_path = base_path 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): if self._platform_data[platform].features_bundle is None: self._platform_data[platform].features_bundle = FeaturesBundle( diff --git a/chrome/common/extensions/docs/server2/samples_data_source.py b/chrome/common/extensions/docs/server2/samples_data_source.py index a692e97..eabcb28 100644 --- a/chrome/common/extensions/docs/server2/samples_data_source.py +++ b/chrome/common/extensions/docs/server2/samples_data_source.py @@ -2,252 +2,77 @@ # 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 import traceback +from data_source import DataSource from extensions_paths import EXAMPLES -import third_party.json_schema_compiler.json_comment_eater as json_comment_eater -import url_constants +from future import All, Future +from platform_util import GetPlatforms -_DEFAULT_ICON_PATH = 'images/sample-default-icon.png' +def _GetSampleId(sample_name): + return sample_name.lower().replace(' ', '-') -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 GetAcceptedLanguages(request): + if request is None: + return [] + accept_language = request.headers.get('Accept-Language', None) + if accept_language is None: + return [] + return [lang_with_q.split(';')[0].strip() + for lang_with_q in accept_language.split(',')] - 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() +def CreateSamplesView(samples_list, request): + return_list = [] + for dict_ in samples_list: + name = dict_['name'] + description = dict_['description'] + if description is None: + description = '' + if name.startswith('__MSG_') or description.startswith('__MSG_'): 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) + # Copy the sample dict so we don't change the dict in the cache. + sample_data = dict_.copy() + name_key = name[len('__MSG_'):-len('__')] + description_key = description[len('__MSG_'):-len('__')] + locale = sample_data['default_locale'] + for lang in GetAcceptedLanguages(request): + if lang in sample_data['locales']: + locale = lang + break + locale_data = sample_data['locales'][locale] + sample_data['name'] = locale_data[name_key]['message'] + sample_data['description'] = locale_data[description_key]['message'] + sample_data['id'] = _GetSampleId(sample_data['name']) + except Exception: + logging.error(traceback.format_exc()) + # Revert the sample to the original dict. + sample_data = dict_ + return_list.append(sample_data) + else: + dict_['id'] = _GetSampleId(name) + return_list.append(dict_) + return return_list - 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 +class SamplesDataSource(DataSource): + '''Constructs a list of samples and their respective files and api calls. + ''' + def __init__(self, server_instance, request): + self._platform_bundle = server_instance.platform_bundle self._request = request - def _GetSampleId(self, sample_name): - return sample_name.lower().replace(' ', '-') + 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 _GetAcceptedLanguages(self): - accept_language = self._request.headers.get('Accept-Language', None) - if accept_language is None: - return [] - return [lang_with_q.split(';')[0].strip() - 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): - if key == 'apps': - samples_list = self._apps_cache.GetFromFileListing('').Get() - else: - samples_list = self._extensions_cache.GetFromFileListing(EXAMPLES).Get() - return_list = [] - for dict_ in samples_list: - name = dict_['name'] - description = dict_['description'] - if description is None: - description = '' - if name.startswith('__MSG_') or description.startswith('__MSG_'): - try: - # Copy the sample dict so we don't change the dict in the cache. - sample_data = dict_.copy() - name_key = name[len('__MSG_'):-len('__')] - description_key = description[len('__MSG_'):-len('__')] - locale = sample_data['default_locale'] - for lang in self._GetAcceptedLanguages(): - if lang in sample_data['locales']: - locale = lang - break - locale_data = sample_data['locales'][locale] - sample_data['name'] = locale_data[name_key]['message'] - sample_data['description'] = locale_data[description_key]['message'] - sample_data['id'] = self._GetSampleId(sample_data['name']) - except Exception as e: - logging.error(traceback.format_exc()) - # Revert the sample to the original dict. - sample_data = dict_ - return_list.append(sample_data) - else: - dict_['id'] = self._GetSampleId(name) - return_list.append(dict_) - return return_list + def get(self, platform): + return self._GetImpl(platform).Get() - def get(self, key): - return { - 'apps': lambda: self._CreateSamplesDict('apps'), - 'extensions': lambda: self._CreateSamplesDict('extensions') - }.get(key, lambda: {})() + def Cron(self): + return All([self._GetImpl(platform) for platform in GetPlatforms()]) diff --git a/chrome/common/extensions/docs/server2/samples_data_source_test.py b/chrome/common/extensions/docs/server2/samples_data_source_test.py deleted file mode 100755 index b798469..0000000 --- a/chrome/common/extensions/docs/server2/samples_data_source_test.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/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() diff --git a/chrome/common/extensions/docs/server2/samples_model.py b/chrome/common/extensions/docs/server2/samples_model.py new file mode 100644 index 0000000..fa66dc3 --- /dev/null +++ b/chrome/common/extensions/docs/server2/samples_model.py @@ -0,0 +1,170 @@ +# 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 diff --git a/chrome/common/extensions/docs/server2/samples_model_test.py b/chrome/common/extensions/docs/server2/samples_model_test.py new file mode 100755 index 0000000..c12f504 --- /dev/null +++ b/chrome/common/extensions/docs/server2/samples_model_test.py @@ -0,0 +1,44 @@ +#!/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() diff --git a/chrome/common/extensions/docs/server2/server_instance.py b/chrome/common/extensions/docs/server2/server_instance.py index 6d3deee..a790821 100644 --- a/chrome/common/extensions/docs/server2/server_instance.py +++ b/chrome/common/extensions/docs/server2/server_instance.py @@ -73,25 +73,8 @@ class ServerInstance(object): self.compiled_fs_factory, host_fs_at_trunk, self.host_file_system_iterator, - self.object_store_creator) - - # 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.object_store_creator, + self.base_path) self.content_providers = ContentProviders( object_store_creator, diff --git a/chrome/common/extensions/docs/server2/template_renderer.py b/chrome/common/extensions/docs/server2/template_renderer.py index ccab9f9..6b7e7c4 100644 --- a/chrome/common/extensions/docs/server2/template_renderer.py +++ b/chrome/common/extensions/docs/server2/template_renderer.py @@ -28,7 +28,7 @@ class TemplateRenderer(object): rendering the 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: render_context = dict((name, d) for name, d in render_context.iteritems() if name in data_sources) @@ -38,15 +38,6 @@ class TemplateRenderer(object): 'extensions_samples_url': EXTENSIONS_SAMPLES, 'static': self._server_instance.base_path + 'static', }) - if additional_context: - render_context.update(additional_context) + render_context.update(additional_context or {}) render_data = template.Render(render_context) 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 |