Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import collections | |
| 5 import re | 6 import re |
| 6 import string | 7 import string |
| 7 import types | 8 import types |
| 9 import urllib | |
| 10 import urlparse | |
| 8 | 11 |
| 9 _ALNUM_CHARS = string.ascii_letters + string.digits | 12 _ALNUM_CHARS = string.ascii_letters + string.digits |
| 10 _SEGMENT_RE_BASE = r'[a-zA-Z0-9][a-zA-Z0-9:_\-.]*' | 13 _SEGMENT_RE_BASE = r'[a-zA-Z0-9][a-zA-Z0-9:_\-.]*' |
| 11 _STREAM_NAME_RE = re.compile('^(' + _SEGMENT_RE_BASE + ')(/' + | 14 _STREAM_NAME_RE = re.compile('^(' + _SEGMENT_RE_BASE + ')(/' + |
| 12 _SEGMENT_RE_BASE + ')*$') | 15 _SEGMENT_RE_BASE + ')*$') |
| 13 _MAX_STREAM_NAME_LENGTH = 4096 | 16 _MAX_STREAM_NAME_LENGTH = 4096 |
| 14 | 17 |
| 15 _MAX_TAG_KEY_LENGTH = 64 | 18 _MAX_TAG_KEY_LENGTH = 64 |
| 16 _MAX_TAG_VALUE_LENGTH = 4096 | 19 _MAX_TAG_VALUE_LENGTH = 4096 |
| 17 | 20 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 98 """ | 101 """ |
| 99 # Alphanumeric check. | 102 # Alphanumeric check. |
| 100 if ch in _ALNUM_CHARS: | 103 if ch in _ALNUM_CHARS: |
| 101 return True | 104 return True |
| 102 if first: | 105 if first: |
| 103 # The first character must be alphanumeric. | 106 # The first character must be alphanumeric. |
| 104 return False | 107 return False |
| 105 | 108 |
| 106 # Check additional middle-name characters: | 109 # Check additional middle-name characters: |
| 107 return ch in ':_-./' | 110 return ch in ':_-./' |
| 111 | |
| 112 | |
| 113 class StreamPath(collections.namedtuple('_StreamPath', ('prefix', 'name'))): | |
| 114 """StreamPath is a full stream path. | |
| 115 | |
| 116 This consists of both a stream prefix and a stream name. | |
| 117 | |
| 118 When constructed with parse or make, the stream path must be completely valid. | |
| 119 However, invalid stream paths may be constructed by manually instantiation. | |
| 120 This can be useful for wildcard query values (e.g., "prefix='foo/*/bar/**'"). | |
| 121 """ | |
| 122 | |
| 123 @classmethod | |
| 124 def make(cls, prefix, name): | |
| 125 """Returns (StreamPath): The validated StreamPath instance. | |
| 126 | |
| 127 Args: | |
| 128 prefix (str): the prefix component | |
| 129 name (str): the name component | |
| 130 | |
| 131 Raises: | |
| 132 ValueError: If path is not a full, valid stream path string. | |
| 133 """ | |
| 134 inst = cls(prefix=prefix, name=name) | |
| 135 inst.validate() | |
| 136 return inst | |
| 137 | |
| 138 @classmethod | |
| 139 def parse(cls, path): | |
| 140 """Returns (StreamPath): The parsed StreamPath instance. | |
| 141 | |
|
jbudorick
2016/10/27 13:24:06
nit: missing Args
dnj
2016/10/27 22:42:45
Done.
| |
| 142 Raises: | |
| 143 ValueError: If path is not a full, valid stream path string. | |
| 144 """ | |
| 145 parts = path.split('/+/', 1) | |
| 146 if len(parts) != 2: | |
| 147 raise ValueError('Not a full stream path: [%s]' % (path,)) | |
| 148 return cls.make(*parts) | |
| 149 | |
| 150 def validate(self): | |
| 151 """Raises: ValueError if this is not a valid stream name.""" | |
| 152 try: | |
| 153 validate_stream_name(self.prefix) | |
| 154 except ValueError as e: | |
| 155 raise ValueError('Invalid prefix component [%s]: %s' % ( | |
| 156 self.prefix, e.message,)) | |
| 157 | |
| 158 try: | |
| 159 validate_stream_name(self.name) | |
| 160 except ValueError as e: | |
| 161 raise ValueError('Invalid name component [%s]: %s' % ( | |
| 162 self.name, e.message,)) | |
| 163 | |
| 164 def __str__(self): | |
| 165 return '%s/+/%s' % (self.prefix, self.name) | |
| 166 | |
| 167 def urlquote(self): | |
| 168 return urllib.quote(str(self), safe='') | |
| 169 | |
| 170 | |
| 171 def get_logdog_viewer_url(host, *stream_paths): | |
| 172 """Returns (str): The LogDog viewer URL for the named stream(s). | |
| 173 | |
| 174 Args: | |
| 175 host (str): The name of the Coordiantor host. | |
| 176 stream_paths: A set of StreamPath instances for the stream paths to | |
| 177 generate the URL for. | |
| 178 """ | |
| 179 if len(stream_paths) == 0: | |
| 180 raise ValueError('At least one stream name must be supplied.') | |
| 181 | |
| 182 return urlparse.urlunparse(( | |
| 183 'https', # Scheme | |
| 184 host, # netloc | |
| 185 'v/', # path | |
| 186 '', # params | |
| 187 '&'.join(('s=%s' % (path.urlquote(),) for path in stream_paths)), # query | |
| 188 '', # fragment | |
| 189 )) | |
| OLD | NEW |