OLD | NEW |
---|---|
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import logging | 5 import logging |
6 import os | 6 import os |
7 import posixpath | |
8 import traceback | |
9 | |
10 from branch_utility import BranchUtility | |
11 from file_system import FileNotFoundError | |
7 from third_party.json_schema_compiler.model import UnixName | 12 from third_party.json_schema_compiler.model import UnixName |
8 import svn_constants | 13 import svn_constants |
9 | 14 |
15 def _SimplifyFileName(file_name): | |
16 return (posixpath.splitext(file_name)[0] | |
17 .lower() | |
18 .replace('.', '') | |
19 .replace('-', '') | |
20 .replace('_', '')) | |
21 | |
10 class PathCanonicalizer(object): | 22 class PathCanonicalizer(object): |
11 '''Transforms paths into their canonical forms. Since the dev server has had | 23 '''Transforms paths into their canonical forms. Since the dev server has had |
12 many incarnations - e.g. there didn't use to be apps/ - there may be old | 24 many incarnations - e.g. there didn't use to be apps/ - there may be old |
13 paths lying around the webs. We try to redirect those to where they are now. | 25 paths lying around the webs. We try to redirect those to where they are now. |
14 ''' | 26 ''' |
15 def __init__(self, channel, compiled_fs_factory): | 27 def __init__(self, compiled_fs_factory): |
16 self._channel = channel | 28 # Map of simplified API names (for typo detection) to their real paths. |
17 self._identity_fs = compiled_fs_factory.CreateIdentity(PathCanonicalizer) | 29 def make_public_apis(_, file_names): |
30 return {_SimplifyFileName(name): name for name in file_names} | |
cduvall
2013/05/15 00:50:50
use dict((...
not at google - send to devlin
2013/05/15 01:26:50
Done.
| |
31 self._public_apis = compiled_fs_factory.Create(make_public_apis, | |
32 PathCanonicalizer) | |
18 | 33 |
19 def Canonicalize(self, path): | 34 def Canonicalize(self, path): |
20 starts_with_channel, path_without_channel = (False, path) | 35 '''Returns the canonical path for |path|, and whether that path is a |
21 if path.startswith('%s/' % self._channel): | 36 permanent canonicalisation (e.g. when we redirect from a channel to a |
22 starts_with_channel, path_without_channel = ( | 37 channel-less URL) or temporary (e.g. when we redirect from an apps-only API |
23 True, path[len(self._channel) + 1:]) | 38 to an extensions one - we may at some point enable it for extensions). |
39 ''' | |
40 class ReturnType(object): | |
41 def __init__(self, path, permanent): | |
42 self.path = path | |
43 self.permanent = permanent | |
44 # Catch incorrect comparisons by disabling ==/!=. | |
45 def __eq__(self): raise NotImplementedError() | |
cduvall
2013/05/15 00:50:50
these need to take another argument or you get a T
not at google - send to devlin
2013/05/15 01:26:50
ah. thanks.
| |
46 def __ne__(self): raise NotImplementedError() | |
24 | 47 |
25 if any(path.startswith(prefix) | 48 # Strip any channel info off it. There are no channels anymore. |
26 for prefix in ('extensions/', 'apps/', 'static/')): | 49 for channel_name in BranchUtility.GetAllChannelNames(): |
27 return path | 50 channel_prefix = channel_name + '/' |
51 if path.startswith(channel_prefix): | |
52 # Redirect now so that we can set the permanent-redirect bit. Channel | |
53 # redirects are the only things that should be permanent redirects; | |
54 # anything else *could* change, so is temporary. | |
55 return ReturnType(path[len(channel_prefix):], True) | |
28 | 56 |
29 if '/' in path_without_channel or path_without_channel == '404.html': | 57 # No further work needed for static. |
30 return path | 58 if path.startswith('static/'): |
59 return ReturnType(path, False) | |
31 | 60 |
32 apps_templates = self._identity_fs.GetFromFileListing( | 61 # People go to just "extensions" or "apps". Redirect to the directory. |
33 '/'.join((svn_constants.PUBLIC_TEMPLATE_PATH, 'apps'))) | 62 if path in ('extensions', 'apps'): |
34 extensions_templates = self._identity_fs.GetFromFileListing( | 63 return ReturnType(path + '/', False) |
35 '/'.join((svn_constants.PUBLIC_TEMPLATE_PATH, 'extensions'))) | |
36 | 64 |
37 if self._channel is None or not starts_with_channel: | 65 # The rest of this function deals with trying to figure out what API page |
38 apps_path = 'apps/%s' % path_without_channel | 66 # for extensions/apps to redirect to, if any. We see a few different cases |
39 extensions_path = 'extensions/%s' % path_without_channel | 67 # here: |
68 # - Unqualified names ("browserAction.html"). These are easy to resolve; | |
69 # figure out whether it's an extension or app API and redirect. | |
70 # - but what if it's both? Well, assume extensions. Maybe later we can | |
71 # check analytics and see which is more popular. | |
72 # - Wrong names ("apps/browserAction.html"). This really does happen, | |
73 # damn it, so do the above logic but record which is the default. | |
74 if any(path.startswith(prefix) for prefix in ('extensions/', 'apps/')): | |
cduvall
2013/05/15 00:50:50
i believe you can do:
path.startswith(('extensions
not at google - send to devlin
2013/05/15 01:26:50
Done.
| |
75 default_platform, reference_path = path.split('/', 1) | |
40 else: | 76 else: |
41 apps_path = '%s/apps/%s' % (self._channel, path_without_channel) | 77 default_platform, reference_path = ('extensions', path) |
42 extensions_path = '%s/extensions/%s' % (self._channel, | |
43 path_without_channel) | |
44 | 78 |
45 unix_path = UnixName(os.path.splitext(path_without_channel)[0]) | 79 try: |
46 if unix_path in extensions_templates: | 80 apps_public = self._public_apis.GetFromFileListing( |
47 return extensions_path | 81 '/'.join((svn_constants.PUBLIC_TEMPLATE_PATH, 'apps'))) |
48 if unix_path in apps_templates: | 82 extensions_public = self._public_apis.GetFromFileListing( |
49 return apps_path | 83 '/'.join((svn_constants.PUBLIC_TEMPLATE_PATH, 'extensions'))) |
50 return extensions_path | 84 except FileNotFoundError: |
85 # Probably offline. | |
86 logging.warning(traceback.format_exc()) | |
87 return ReturnType(path, False) | |
88 | |
89 simple_reference_path = _SimplifyFileName(reference_path) | |
90 apps_path = apps_public.get(simple_reference_path) | |
91 extensions_path = extensions_public.get(simple_reference_path) | |
92 | |
93 if apps_path is None: | |
94 if extensions_path is None: | |
95 # No idea. Just return the original path. It'll probably 404. | |
96 pass | |
97 else: | |
98 path = 'extensions/%s' % extensions_path | |
99 else: | |
100 if extensions_path is None: | |
101 path = 'apps/%s' % apps_path | |
102 else: | |
103 assert apps_path == extensions_path | |
104 path = '%s/%s' % (default_platform, apps_path) | |
105 | |
106 return ReturnType(path, False) | |
OLD | NEW |