| # Copyright 2013 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 logging |
| import os |
| import posixpath |
| import traceback |
| |
| from branch_utility import BranchUtility |
| from file_system import FileNotFoundError |
| from third_party.json_schema_compiler.model import UnixName |
| import svn_constants |
| |
| def _SimplifyFileName(file_name): |
| return (posixpath.splitext(file_name)[0] |
| .lower() |
| .replace('.', '') |
| .replace('-', '') |
| .replace('_', '')) |
| |
| class PathCanonicalizer(object): |
| '''Transforms paths into their canonical forms. Since the dev server has had |
| many incarnations - e.g. there didn't use to be apps/ - there may be old |
| paths lying around the webs. We try to redirect those to where they are now. |
| ''' |
| def __init__(self, compiled_fs_factory): |
| # Map of simplified API names (for typo detection) to their real paths. |
| def make_public_apis(_, file_names): |
| return dict((_SimplifyFileName(name), name) for name in file_names) |
| self._public_apis = compiled_fs_factory.Create(make_public_apis, |
| PathCanonicalizer) |
| |
| def Canonicalize(self, path): |
| '''Returns the canonical path for |path|, and whether that path is a |
| permanent canonicalisation (e.g. when we redirect from a channel to a |
| channel-less URL) or temporary (e.g. when we redirect from an apps-only API |
| to an extensions one - we may at some point enable it for extensions). |
| ''' |
| class ReturnType(object): |
| def __init__(self, path, permanent): |
| self.path = path |
| self.permanent = permanent |
| |
| # Catch incorrect comparisons by disabling ==/!=. |
| def __eq__(self, _): raise NotImplementedError() |
| def __ne__(self, _): raise NotImplementedError() |
| |
| # Strip any channel info off it. There are no channels anymore. |
| for channel_name in BranchUtility.GetAllChannelNames(): |
| channel_prefix = channel_name + '/' |
| if path.startswith(channel_prefix): |
| # Redirect now so that we can set the permanent-redirect bit. Channel |
| # redirects are the only things that should be permanent redirects; |
| # anything else *could* change, so is temporary. |
| return ReturnType(path[len(channel_prefix):], True) |
| |
| # No further work needed for static. |
| if path.startswith('static/'): |
| return ReturnType(path, False) |
| |
| # People go to just "extensions" or "apps". Redirect to the directory. |
| if path in ('extensions', 'apps'): |
| return ReturnType(path + '/', False) |
| |
| # The rest of this function deals with trying to figure out what API page |
| # for extensions/apps to redirect to, if any. We see a few different cases |
| # here: |
| # - Unqualified names ("browserAction.html"). These are easy to resolve; |
| # figure out whether it's an extension or app API and redirect. |
| # - but what if it's both? Well, assume extensions. Maybe later we can |
| # check analytics and see which is more popular. |
| # - Wrong names ("apps/browserAction.html"). This really does happen, |
| # damn it, so do the above logic but record which is the default. |
| if path.startswith(('extensions/', 'apps/')): |
| default_platform, reference_path = path.split('/', 1) |
| else: |
| default_platform, reference_path = ('extensions', path) |
| |
| try: |
| apps_public = self._public_apis.GetFromFileListing( |
| '/'.join((svn_constants.PUBLIC_TEMPLATE_PATH, 'apps'))) |
| extensions_public = self._public_apis.GetFromFileListing( |
| '/'.join((svn_constants.PUBLIC_TEMPLATE_PATH, 'extensions'))) |
| except FileNotFoundError: |
| # Probably offline. |
| logging.warning(traceback.format_exc()) |
| return ReturnType(path, False) |
| |
| simple_reference_path = _SimplifyFileName(reference_path) |
| apps_path = apps_public.get(simple_reference_path) |
| extensions_path = extensions_public.get(simple_reference_path) |
| |
| if apps_path is None: |
| if extensions_path is None: |
| # No idea. Just return the original path. It'll probably 404. |
| pass |
| else: |
| path = 'extensions/%s' % extensions_path |
| else: |
| if extensions_path is None: |
| path = 'apps/%s' % apps_path |
| else: |
| assert apps_path == extensions_path |
| path = '%s/%s' % (default_platform, apps_path) |
| |
| return ReturnType(path, False) |